diff --git a/buildall.sh b/buildall.sh new file mode 100755 index 0000000..a3117cd --- /dev/null +++ b/buildall.sh @@ -0,0 +1,5 @@ +#!/bin/bash +for dir in $(find -maxdepth 1 -not -path '*/\.*' -not -path '\.' -type d); do + echo lua makepkg.lua "$dir" "$1" "$(git rev-parse --short HEAD)" + lua makepkg.lua "$dir" "$1" "$(git rev-parse --short HEAD)" +done diff --git a/lzss.lua b/lzss.lua new file mode 100644 index 0000000..501d0ec --- /dev/null +++ b/lzss.lua @@ -0,0 +1,123 @@ +--[[---------------------------------------------------------------------------- + + LZSS - encoder / decoder + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + +--]]---------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +local M = {} +local string, table = string, table + +-------------------------------------------------------------------------------- +local POS_BITS = 12 +local LEN_BITS = 16 - POS_BITS +local POS_SIZE = 1 << POS_BITS +local LEN_SIZE = 1 << LEN_BITS +local LEN_MIN = 3 + +-------------------------------------------------------------------------------- +function M.compress(input) + local offset, output = 1, {} + local window = '' + + local function search() + for i = LEN_SIZE + LEN_MIN - 1, LEN_MIN, -1 do + local str = string.sub(input, offset, offset + i - 1) + local pos = string.find(window, str, 1, true) + if pos then + return pos, str + end + end + end + + while offset <= #input do + local flags, buffer = 0, {} + + for i = 0, 7 do + if offset <= #input then + local pos, str = search() + if pos and #str >= LEN_MIN then + local tmp = ((pos - 1) << LEN_BITS) | (#str - LEN_MIN) + buffer[#buffer + 1] = string.pack('>I2', tmp) + else + flags = flags | (1 << i) + str = string.sub(input, offset, offset) + buffer[#buffer + 1] = str + end + window = string.sub(window .. str, -POS_SIZE) + offset = offset + #str + else + break + end + end + + if #buffer > 0 then + output[#output + 1] = string.char(flags) + output[#output + 1] = table.concat(buffer) + end + end + + return table.concat(output) +end + +-------------------------------------------------------------------------------- +function M.decompress(input) + local offset, output = 1, {} + local window = '' + + while offset <= #input do + local flags = string.byte(input, offset) + offset = offset + 1 + + for i = 1, 8 do + local str = nil + if (flags & 1) ~= 0 then + if offset <= #input then + str = string.sub(input, offset, offset) + offset = offset + 1 + end + else + if offset + 1 <= #input then + local tmp = string.unpack('>I2', input, offset) + offset = offset + 2 + local pos = (tmp >> LEN_BITS) + 1 + local len = (tmp & (LEN_SIZE - 1)) + LEN_MIN + str = string.sub(window, pos, pos + len - 1) + end + end + flags = flags >> 1 + if str then + output[#output + 1] = str + window = string.sub(window .. str, -POS_SIZE) + end + end + end + + return table.concat(output) +end + +return M diff --git a/makepkg.lua b/makepkg.lua new file mode 100644 index 0000000..842a26f --- /dev/null +++ b/makepkg.lua @@ -0,0 +1,97 @@ +local serial = require "serialization" +local w, lzss = pcall(require,"lzss") +if not w then lzss = nil end +local tArgs = {...} +local fsnames = {"lfs","luafilesystem","filesystem"} +local w, fs +for k,v in pairs(fsnames) do + w,fs = pcall(require,v) + if w then break end +end +if not w then error("luafilesystem not detected") end + +local function cint(n,l) + local t={} + for i = 0, 7 do + t[i+1] = (n >> (i * 8)) & 0xFF + end + return string.reverse(string.char(table.unpack(t)):sub(1,l)) +end + +local function genHeader(fname,len) + return string.format("%s%s%s",cint(fname:len(),2),fname,cint(len,2)) +end + +local function fnormalize(path) + local rt = {} + for segment in path:gmatch("[^/]+") do + if segment ~= "." and segment ~= ".." then + rt[#rt+1] = segment + end + end + return table.concat(rt,"/") +end + +local dirs, files = {tArgs[1]},{} +for _,dir in ipairs(dirs) do + for file in fs.dir(dir) do + if file == "." or file == ".." then + elseif fs.attributes(dir.."/"..file).mode == "directory" then + dirs[#dirs+1] = dir.."/"..file + elseif file ~= "package.cfg" then + files[#files+1] = dir.."/"..file + end + end +end + +local f = io.open(tArgs[1].."/package.cfg","rb") +if not f then error("no package.cfg in source") end +local pkginfo = serial.unserialize(f:read("*a")) +f:close() + +local outpath = tArgs[2].."/"..tArgs[1]..".mtar" + +local of = io.open(outpath,"wb") +for k,v in ipairs(files) do + local f = io.open(v,"rb") + if f then + local content = f:read("*a") + f:close() + of:write(genHeader(fnormalize(v:sub(tArgs[1]:len()+1)),content:len())) + of:write(content) + end +end +of:write(string.char(0):rep(2)) +of:close() + +if lzss then + print("Compressing "..outpath.." to "..outpath..".lss") + local f = io.open(outpath,"rb") + local of = io.open(outpath..".lss","wb") + while true do + local b = f:read(4096) + if not b or b:len() < 1 then break end + b = lzss.compress(b) + of:write(b) + end + f:close() + of:close() + outpath = outpath .. ".lss" + pkginfo.compressed = true +end +pkginfo.filename = fnormalize(outpath:sub(tArgs[2]:len()+1)) +pkginfo.version = tArgs[3] + +local repo = {} +local f = io.open(tArgs[2].."/packages.cfg","rb") +if f then + repo = serial.unserialize(f:read("*a")) or {} + f:close() +end +repo[tArgs[1]] = pkginfo + +local f = io.open(tArgs[2].."/packages.cfg","wb") +if f then + f:write(serial.serialize(repo)) + f:close() +end diff --git a/serialization.lua b/serialization.lua new file mode 100644 index 0000000..9dca9f4 --- /dev/null +++ b/serialization.lua @@ -0,0 +1,50 @@ +local serial = {} +local local_pairs=function(tbl) + local mt=getmetatable(tbl) + return (mt and mt.__pairs or pairs)(tbl) +end +function serial.serialize(value,af) -- boolean -- string -- serialize *value* into a string. If *af* is true, allow functions. This breaks unserialization. + local kw={["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["goto"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true} + local id="^[%a_][%w_]*$" + local ts={} + local function s(v,l) + local t=type(v) + if t=="nil" then return "nil" + elseif t=="boolean" then return v and "true" or "false" + elseif t=="number" then + if v~=v then return "0/0" + elseif v==math.huge then return "math.huge" + elseif v==-math.huge then return "-math.huge" + else return tostring(v) end + elseif t=="string" then return string.format("%q",v):gsub("\\\n","\\n") + elseif t=="table" then + if ts[v] then error("tcyc") end + ts[v]=true + local i,r=1, nil + local f=table.pack(local_pairs(v)) + for k,v in table.unpack(f) do + if r then r=r..","..(("\n"..string.rep(" ",l)) or "") + else r="{" end + local tk=type(k) + if tk=="number" and k==i then + i=i+1 + r=r..s(v,l+1) + else + if tk == "string" and not kw[k] and string.match(k,id) then r=r..k + else r=r.."["..s(k,l+1).."]" end + r=r.."="..s(v,l+1) end end + ts[v]=nil + return (r or "{").."}" + elseif t=="function" and af then + return tostring(v) + else error("ut "..t) end end + return s(value, 1) +end +function serial.unserialize(data) -- string -- -- return *data*, but unserialized + local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}}) + if not result then return nil, reason end + local ok, output = pcall(result) + if not ok then return nil, output end + return output +end +return serial