1
0
Fork 0
knot-resolver/scripts/map_install_src.lua
Daniel Baumann fbc604e215
Adding upstream version 5.7.5.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:56:17 +02:00

168 lines
4.4 KiB
Lua
Executable file

#!/usr/bin/env luajit
-- SPDX-License-Identifier: GPL-3.0-or-later
-- parse install commands from stdin
-- input: PREFIX=... make install --dry-run --always-make
-- output: <install path> <source path>
-- (or sed commands if --sed was specified)
output = 'list'
if #arg > 1 or arg[1] == '-h' or arg[1] == '--help' then
print(string.format([[
Read install commands and map install paths to paths in source directory.
Usage:
$ PREFIX=... make install --dry-run --always-make | %s
Example output:
/kresd/git/.local/lib/kdns_modules/policy.lua modules/policy/policy.lua
Option --sed will produce output suitable as input suitable for sed.]],
arg[0]))
os.exit(1)
elseif #arg == 0 then
output = 'list'
elseif arg[1] == '--sed' then
output = 'sed'
else
print('Invalid arguments. See --help.')
os.exit(2)
end
-- remove double // from paths and remove trailing /
function normalize_path(path)
assert(path)
repeat
path, changes = path:gsub('//', '/')
until changes == 0
return path:gsub('/$', '')
end
function is_opt(word)
return word:match('^-')
end
-- opts requiring additional argument to be skipped
local ignored_opts_with_arg = {
['--backup'] = true,
['-g'] = true,
['--group'] = true,
['-m'] = true,
['--mode'] = true,
['-o'] = true,
['--owner'] = true,
['--strip-program'] = true,
['--suffix'] = true,
}
-- state machine junctions caused by --opts
-- returns: new state (expect, mode) and target name if any
function parse_opts(word, expect, mode)
if word == '--' then
return 'names', mode, nil -- no options anymore
elseif word == '-d' or word == '--directory' then
return 'opt_or_name', 'newdir', nil
elseif word == '-t' or word == '--target-directory' then
return 'targetdir', mode, nil
elseif word:match('^--target-directory=') then
return 'opt_or_name', mode, string.sub(word, 20)
elseif ignored_opts_with_arg[word] then
return 'ignore', mode, nil -- ignore next word
else
return expect, mode, nil -- unhandled opt
end
end
-- cmd: complete install command line: install -m 0644 -t dest src1 src2
-- dirs: names known to be directories: name => true
-- returns: updated dirs
function process_cmd(cmd, dirs)
-- print('# ' .. cmd)
sanity_check(cmd)
local expect = 'install'
local mode = 'copy' -- copy or newdir
local target -- last argument or argument for install -t
local names = {} -- non-option arguments
for word in cmd:gmatch('%S+') do
if expect == 'install' then -- parsing 'install'
assert(word == 'install')
expect = 'opt_or_name'
elseif expect == 'opt_or_name' then
if is_opt(word) then
expect, mode, newtarget = parse_opts(word, expect, mode)
target = newtarget or target
else
if mode == 'copy' then
table.insert(names, word)
elseif mode == 'newdir' then
local path = normalize_path(word)
dirs[path] = true
else
assert(false, 'bad mode')
end
end
elseif expect == 'targetdir' then
local path = normalize_path(word)
dirs[path] = true
target = word
expect = 'opt_or_name'
elseif expect == 'names' then
table.insert(names, word)
elseif expect == 'ignore' then
expect = 'opt_or_name'
else
assert(false, 'bad expect')
end
end
if mode == 'newdir' then
-- no mapping to print, this cmd just created directory
return dirs
end
if not target then -- last argument is the target
target = table.remove(names)
end
assert(target, 'fatal: no target in install cmd')
target = normalize_path(target)
for _, name in pairs(names) do
basename = string.gsub(name, "(.*/)(.*)", "%2")
if not dirs[target] then
print('fatal: target directory "' .. target .. '" was not created yet!')
os.exit(2)
end
-- mapping installed name -> source name
if output == 'list' then
print(target .. '/' .. basename, name)
elseif output == 'sed' then
print(string.format([[s`%s`%s`g]],
target .. '/' .. basename, name))
else
assert(false, 'unsupported output')
end
end
return dirs
end
function sanity_check(cmd)
-- shell quotation is not supported
assert(not cmd:match('"'), 'quotes " are not supported')
assert(not cmd:match("'"), "quotes ' are not supported")
assert(not cmd:match('\\'), "escapes like \\ are not supported")
assert(cmd:match('^install%s'), 'not an install command')
end
-- remember directories created by install -d so we can expand relative paths
local dirs = {}
while true do
local cmd = io.read("*line")
if not cmd then
break
end
local isinstall = cmd:match('^install%s')
if isinstall then
dirs = process_cmd(cmd, dirs)
end
end