You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
382 lines
9.6 KiB
382 lines
9.6 KiB
|
|
local debugls = function(l) for k, v in pairs(l) do print("dbg: " .. tostring(k) .. " => " .. tostring(v)) end end
|
|
|
|
-- Not portable
|
|
local separate_path = function(path)
|
|
for i = #path,1,-1 do
|
|
if string.sub(path, i, i) == "/" then
|
|
return { dir = string.sub(path, 1, i), file = string.sub(path, i + 1, #path) }
|
|
end
|
|
end
|
|
return { file = path }
|
|
end
|
|
|
|
-- Not portable
|
|
local to_dir_name = function(s)
|
|
if string.sub(s, #s, #s) ~= "/" then
|
|
return s .. "/"
|
|
else
|
|
return s
|
|
end
|
|
end
|
|
|
|
-- Not portable
|
|
local abs_filename = function(cwd, path)
|
|
if string.sub(path, 1, 1) == "/" then
|
|
return path
|
|
else
|
|
if string.sub(cwd, #cwd, #cwd) ~= "/" then
|
|
return to_dir_name(cwd) .. path
|
|
else
|
|
return to_dir_name(cwd) .. path
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Not portable
|
|
local normalize_abs_path = function(path, isdir)
|
|
local names = {}
|
|
for nm in string.gmatch(path, "[^/]+") do
|
|
names[#names + 1] = nm
|
|
end
|
|
|
|
-- Normalize away `.`
|
|
for i = #names,1,-1 do
|
|
if names[i] == "." or names[i] == "" then
|
|
table.remove(names, i)
|
|
end
|
|
end
|
|
|
|
-- Normalize away `..`
|
|
while names[1] == ".." do table.remove(names, 1) end
|
|
local c = true
|
|
while c do
|
|
c = false
|
|
for i = 1,(#names - 1) do
|
|
if names[i + 1] == ".." then
|
|
table.remove(names, i)
|
|
table.remove(names, i)
|
|
c = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if isdir then
|
|
return "/" .. table.concat(names, "/") .. "/"
|
|
else
|
|
return "/" .. table.concat(names, "/")
|
|
end
|
|
end
|
|
|
|
local path_size = function(path)
|
|
local i = 0
|
|
for _ in string.gmatch(path, "[^/]+") do
|
|
i = i + 1
|
|
end
|
|
return i
|
|
end
|
|
|
|
-- Not portable
|
|
local is_dir = function(filename)
|
|
local c = false
|
|
local p = io.popen("[ -d '" .. filename .. "' ] && echo ''")
|
|
for l in p:lines() do c = true end
|
|
p:close()
|
|
return c
|
|
end
|
|
|
|
-- Not portable
|
|
local file_exists = function(filename)
|
|
local c = false
|
|
local p = io.popen("[ -e '" .. filename .. "' ] && echo ''")
|
|
for l in p:lines() do c = true end
|
|
p:close()
|
|
return c
|
|
end
|
|
|
|
-- Not portable
|
|
local ls_dir = function(dirpath)
|
|
local t = {}
|
|
local p = io.popen("ls -A '" .. dirpath .. "'")
|
|
for l in p:lines() do t[#t + 1] = l end
|
|
p:close()
|
|
return t
|
|
end
|
|
|
|
local read_manifest
|
|
read_manifest = function(filename, file_table, errout)
|
|
local parse_line = function(line, cwd)
|
|
local weight_specifier
|
|
local filepath
|
|
|
|
for i = 1,#line do
|
|
if string.sub(line, i, i) == " " then
|
|
weight_specifier = string.sub(line, 1, i - 1)
|
|
filepath = string.sub(line, i + 1, #line)
|
|
break
|
|
end
|
|
end
|
|
|
|
if weight_specifier == nil and filepath == nil then
|
|
return nil
|
|
end
|
|
|
|
-- True if a category is to be weighted as a unit, thereby reducing the effective
|
|
-- weight of each of its entries. This flag is meant to be employed for other manifests.
|
|
local weight_whole_category = false
|
|
-- Indicates that the entry is a directory whose applicable files should be read in.
|
|
local directory = false
|
|
-- This indicates that the entry is a manifest to be read in.
|
|
local manifest = false
|
|
-- This is the level to recurse into directories or the `here` case.
|
|
-- `nil` indicates unbounded recursion.
|
|
local recursion_level = 0
|
|
-- When `weight_whole_category` is on, this specifies how deeply the categorical
|
|
-- weighting will apply. Again, `nil` is unbounded.
|
|
local recursive_weighting_level = 0
|
|
|
|
local numstr = ""
|
|
|
|
local symbol_actions =
|
|
{
|
|
["#"] = function() weight_whole_category = true end,
|
|
["?"] = function() manifest = true end,
|
|
["/"] = function() if recursion_level ~= nil then recursion_level = recursion_level + 1 end end,
|
|
["*"] = function() recursion_level = nil end,
|
|
["$"] = function() if recursive_weighting_level ~= nil then recursive_weighting_level = recursive_weighting_level + 1 end end,
|
|
["%"] = function() recursive_weighting_level = nil end,
|
|
["0"] = true,
|
|
["1"] = true,
|
|
["2"] = true,
|
|
["3"] = true,
|
|
["4"] = true,
|
|
["5"] = true,
|
|
["6"] = true,
|
|
["7"] = true,
|
|
["8"] = true,
|
|
["9"] = true,
|
|
["-"] = true,
|
|
["."] = true,
|
|
["e"] = true,
|
|
}
|
|
|
|
for i = 1,#weight_specifier do
|
|
local sym = string.sub(weight_specifier, i, i)
|
|
local action = symbol_actions[sym]
|
|
if action == true then
|
|
numstr = numstr .. sym
|
|
elseif action == nil then
|
|
else
|
|
action()
|
|
end
|
|
end
|
|
|
|
local num = tonumber(numstr)
|
|
if num == nil then num = 1 end
|
|
|
|
return
|
|
{
|
|
weight = num,
|
|
manifest = manifest,
|
|
catweight = weight_whole_category,
|
|
directory = directory,
|
|
rclevel = recursion_level,
|
|
rcwlevel = recursive_weighting_level,
|
|
file = normalize_abs_path(abs_filename(cwd, filepath), false)
|
|
}
|
|
end
|
|
|
|
local is_iv_name = function(s) return string.sub(s, -4, -1) == ".ivz" or string.sub(s, -3, -1) == ".iv" end
|
|
|
|
local descend_dir
|
|
descend_dir = function(dirpath, file_table, total_weight, catweight, rclevel, rcwlevel, source)
|
|
local ftab = ls_dir(dirpath)
|
|
local nrcl
|
|
if rclevel == nil then nrcl = nil else nrcl = rclevel - 1 end
|
|
if catweight then
|
|
if rcwlevel == 0 then
|
|
local temp_table = {}
|
|
for _,en in pairs(ftab) do
|
|
local iv = normalize_abs_path(abs_filename(dirpath, en), false)
|
|
if is_dir(iv) and (rclevel == nil or rclevel > 0) then
|
|
descend_dir(to_dir_name(iv), temp_table, 1, false, nrcl, 0, source)
|
|
elseif is_iv_name(iv) then
|
|
temp_table[#temp_table + 1] =
|
|
{
|
|
file = iv,
|
|
-- Will be overwritten by loop below anyway
|
|
}
|
|
end
|
|
end
|
|
for _,ent in pairs(temp_table) do
|
|
file_table[#file_table + 1] =
|
|
{
|
|
file = ent.file,
|
|
weight = total_weight / #temp_table,
|
|
source = source
|
|
}
|
|
end
|
|
else
|
|
for _,en in pairs(ftab) do
|
|
local iv = normalize_abs_path(abs_filename(dirpath, en))
|
|
if is_dir(iv) and (rclevel == nil or rclevel > 0) then
|
|
if rcwlevel == nil then
|
|
descend_dir(to_dir_name(iv), file_table, total_weight / #ftab, true, nrcl, nil, source)
|
|
else
|
|
descend_dir(to_dir_name(iv), file_table, total_weight / #ftab, true, nrcl, rcwlevel - 1, source)
|
|
end
|
|
elseif is_iv_name(iv) then
|
|
file_table[#file_table + 1] =
|
|
{
|
|
file = iv,
|
|
weight = total_weight / #ftab,
|
|
source = source
|
|
}
|
|
end
|
|
end
|
|
end
|
|
return total_weight
|
|
else
|
|
local twc = 0
|
|
for _,en in pairs(ftab) do
|
|
local iv = normalize_abs_path(abs_filename(dirpath, en))
|
|
if is_dir(iv) and (rclevel == nil or rclevel > 0) then
|
|
descend_dir(to_dir_name(iv), file_table, total_weight, false, nrcl, 0, source)
|
|
elseif is_iv_name(iv) then
|
|
twc = twc + total_weight
|
|
file_table[#file_table + 1] =
|
|
{
|
|
file = iv,
|
|
weight = total_weight,
|
|
source = source
|
|
}
|
|
end
|
|
end
|
|
return twc
|
|
end
|
|
end
|
|
|
|
if filename == nil then return nil end
|
|
|
|
local mf = io.open(filename, "r")
|
|
if not mf then errout("Could not open manifest " .. filename) return nil end
|
|
local total_weight = 0
|
|
|
|
for line in mf:lines() do
|
|
local entry = parse_line(line, separate_path(filename).dir)
|
|
if entry == nil then goto endloop end
|
|
|
|
if entry.manifest then
|
|
if not file_exists(entry.file) or is_dir(entry.file) then
|
|
errout("warning: file " .. entry.file .. " is not a normal file")
|
|
end
|
|
local temp_table = {}
|
|
local tw = read_manifest(entry.file, temp_table, errout)
|
|
if entry.catweight then
|
|
total_weight = total_weight + entry.weight
|
|
for _,ent in pairs(temp_table) do
|
|
file_table[#file_table + 1] =
|
|
{
|
|
file = ent.file,
|
|
weight = entry.weight * ent.weight / tw,
|
|
source = ent.source,
|
|
}
|
|
end
|
|
else
|
|
for _,ent in pairs(temp_table) do
|
|
total_weight = total_weight + entry.weight * ent.weight
|
|
file_table[#file_table + 1] =
|
|
{
|
|
file = ent.file,
|
|
weight = entry.weight * ent.weight,
|
|
source = ent.source,
|
|
}
|
|
end
|
|
end
|
|
else
|
|
if not file_exists(entry.file) then
|
|
errout("warning: file " .. entry.file .. " does not exist")
|
|
end
|
|
if is_dir(entry.file) then
|
|
total_weight = total_weight + descend_dir(to_dir_name(entry.file), file_table, entry.weight, entry.catweight, entry.rclevel, entry.rcwlevel, { file = entry.file, manifest = normalize_abs_path(filename) })
|
|
else
|
|
total_weight = total_weight + entry.weight
|
|
file_table[#file_table + 1] =
|
|
{
|
|
file = entry.file,
|
|
weight = entry.weight,
|
|
source = { file = entry.file, manifest = normalize_abs_path(filename) }
|
|
}
|
|
end
|
|
end
|
|
|
|
::endloop::
|
|
end
|
|
|
|
mf:close()
|
|
|
|
return total_weight
|
|
end
|
|
|
|
local assemble_weights = function(entries, errout)
|
|
local ivs = {}
|
|
|
|
for _, ent in pairs(entries) do
|
|
if ivs[ent.file] ~= nil then
|
|
local sza = path_size(ivs[ent.file].source.file)
|
|
local szb = path_size(ent.source.file)
|
|
if sza < szb then
|
|
ivs[ent.file] = { weight = ent.weight, source = ent.source }
|
|
elseif sza == szb then
|
|
local namea = ivs[ent.file].source.manifest
|
|
local nameb = ent.source.manifest
|
|
if namea == nameb then
|
|
errout("warning: two conflicting weights for " .. ent.file .. " in " .. namea)
|
|
else
|
|
errout("warning: two conflicting weights for " .. ent.file .. " in " .. namea .. " and " .. nameb)
|
|
end
|
|
end
|
|
else
|
|
ivs[ent.file] = { weight = ent.weight, source = ent.source }
|
|
end
|
|
end
|
|
|
|
return ivs
|
|
end
|
|
|
|
local function usage()
|
|
print("usage: <this program> <input manifest> <output table>")
|
|
end
|
|
|
|
if arg[1] == nil or arg[2] == nil then
|
|
usage()
|
|
return
|
|
end
|
|
|
|
local taboutf = io.open(arg[2], "w")
|
|
if taboutf == nil then
|
|
print("Could not open output file")
|
|
return
|
|
end
|
|
|
|
local tab = {}
|
|
|
|
read_manifest(arg[1], tab, function(s) print(s) end)
|
|
|
|
local weightmap = assemble_weights(tab, function(s) print(s) end)
|
|
|
|
local cumweights = {}
|
|
local cum = 0.0
|
|
|
|
for file, wt in pairs(weightmap) do
|
|
cum = cum + wt.weight
|
|
table.insert(cumweights, { cum = cum, file = file, wt = wt })
|
|
end
|
|
|
|
for idx, ent in ipairs(cumweights) do
|
|
taboutf:write(tostring(ent.cum) .. " : " .. ent.file .. "\n")
|
|
end
|
|
|
|
taboutf:close()
|
|
|