Last active
October 21, 2018 15:34
-
-
Save zr-tex8r/d7d0788d5e2c0f987c978e25f484ba8e to your computer and use it in GitHub Desktop.
Lua: SATySFi用のupdmapみたいなやつ(未完成)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# stupdmap.cfg | |
# To be placed in ~/.satysfi/dist/stupdmap | |
Nishiki-teki = C:/windows/fonts/nishiki-teki.ttf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | |
-- This is file 'stupdmap.lua'. | |
-- | |
-- Copyright (c) 2018 Takayuki YATO (aka. "ZR") | |
-- GitHub: https://github.com/zr-tex8r | |
-- Twitter: @zr_tex8r | |
-- | |
-- This package is distributed under the MIT License. | |
-- | |
prog_name = "stupdmap" | |
version = "0.1.5" | |
mod_date = "2018-07-16" | |
---------------------------------------- global parameters | |
verbose = 0 | |
dry_run = false | |
locate = true | |
symlink = false | |
cfg_file = nil | |
hash_dir = 'dist/hash' | |
fonts_dir = 'dist/fonts' | |
stupdmap_dir = 'dist/stupdmap' | |
default_cfg_file = 'stupdmap.cfg' | |
---------------------------------------- helpers | |
lfs = require "lfs" | |
do | |
unpack = unpack or table.unpack | |
insert = table.insert | |
is_file = lfs.isfile or function (path) | |
return (lfs.attributes(path, 'mode') == 'file') | |
end | |
is_dir = lfs.isdir or function (path) | |
return (lfs.attributes(path, 'mode') == 'directory') | |
end | |
is_win = (function() | |
local o = os.type or (jit and jit.os) or (ffi and ffi.os) | |
if o then return (o:lower() == 'windows') end | |
o = (os.getenv('OS') or ''):lower() | |
return (o:match('^windows') ~= nil) | |
end)() | |
function is_abspath(pname) | |
if is_win then | |
return (pname:match("^%w:") ~= nil or pname:match("^[/\\]") ~= nil) | |
else return (pname:match("^/") ~= nil) | |
end | |
end | |
function unxpath(pname) | |
pname = tostring(pname) | |
return (is_win) and pname:gsub('\\', '/') or pname | |
end | |
function natpath(pname) | |
pname = tostring(pname) | |
return (is_win) and pname:gsub('/', '\\') or pname | |
end | |
function mkpath(...) | |
return concat({...}, '/') | |
end | |
function mkdir(bpath, path) | |
if dry_run then return end | |
for p in path:gmatch('[^/]+') do | |
bpath = mkpath(bpath, p) | |
if not (is_dir(bpath) or lfs.mkdir(bpath)) then | |
abort('cannot make directory', bpath) | |
end | |
end | |
end | |
function read_whole(path) | |
local h = sure(io.open(path, 'rb'), "cannot open for input", path) | |
local d = sure(h:read('*a'), "cannot read from file", path) | |
h:close() | |
return d | |
end | |
function write_whole(path, data) | |
local d = tostring(data or "") | |
log(1, "write "..tostring(#d).." bytes to", path) | |
if dry_run then return end | |
local h = sure(io.open(path, 'wb'), "cannot open for output", path) | |
sure(h:write(d or ""), "cannot write to file", path) | |
h:close() | |
end | |
function str(val) | |
return (type(val) == 'table') and '{'..concat(val, ',')..'}' | |
or tostring(val) | |
end | |
function concat(tbl, ...) | |
local t = {} | |
for i = 1, #tbl do t[i] = str(tbl[i]) end | |
return table.concat(t, ...) | |
end | |
function append(tbl1, tbl2) | |
local t = { unpack(tbl1) } | |
for _, v in ipairs(tbl2) do table.insert(t, v) end | |
return t | |
end | |
end | |
---------------------------------------- search path | |
do | |
user_root = (function() | |
local p = (is_win) and unxpath(os.getenv('USERPROFILE') or '') | |
or (os.getenv('HOME') or '') | |
return (p == '') and {} or { p..'/.satysfi' } | |
end)() | |
sys_root = (function() | |
if is_win then | |
local p = unxpath(os.getenv('SATYSFI_RUNTIME') or '') | |
return (p == '') and {} or { p } | |
else | |
return { '/usr/local/share/satysfi', '/usr/share/satysfi' } | |
end | |
end)() | |
all_root = append(user_root, sys_root) | |
function find_file(path, root) | |
if is_abspath(path) then | |
log(2, "find_file", "is an absolute path", path) | |
return nil | |
end | |
log(2, "find_file", path) | |
for _, r in ipairs(root) do | |
local p = mkpath(r, path) | |
log(2, 'find_file', 'search path', p) | |
if is_file(p) then | |
log(1, 'find_file', 'found', p) | |
return p | |
end | |
end | |
end | |
end | |
---------------------------------------- config file | |
do | |
function get_cfg_path() | |
if cfg_file and is_file(cfg_file) then | |
log(1, "cfg file found", cfg_file) | |
return cfg_file | |
end | |
local fcfg = mkpath(stupdmap_dir, cfg_file or default_cfg_file) | |
log(1, "search path for cfg file", fcfg) | |
return sure(find_file(fcfg, user_root), | |
"file not found on search path", fcfg) | |
end | |
function read_cfg_file(pcfg) | |
log(1, "read cfg file", pcfg) | |
local hcfg = sure(io.open(pcfg, 'rb'), | |
"cannot read from file", pcfg) | |
local cfg, lno = {}, 0 | |
while true do | |
local line = hcfg:read('*l') | |
if not line then break end | |
lno = lno + 1; local msglno = "line "..tostring(lno) | |
line = line:gsub('^%s+', ''):gsub('%s+$', ''):gsub('%s', ' ') | |
if not (line == '' or line:match('^[%%%#]')) then | |
sure(not (line:find('\0', 1, true) or line:find('[\1-\31]')), | |
"bad character in cfg file", msglno) | |
local key, val = line:match("^([^ \"\'=]+) *= *(.+)") | |
sure(key, "error in cfg file", msglno, line) | |
log(2, msglno, key, val) | |
insert(cfg, {name=key, path=val}) | |
end | |
end | |
log(1, "total "..tostring(#cfg).." entires", pcfg) | |
return cfg | |
end | |
function resolve_location(cfg) | |
log(1, "resolve font location") | |
for _, e in ipairs(cfg) do | |
local dn, fn = e.path:match('(.*)/(.*)') | |
if dn then e.dir, e.file = dn, fn | |
else e.dir, e.file = '.', e.path | |
end | |
log(1, "resolve", "dir="..e.dir, "file="..e.file) | |
end | |
end | |
end | |
---------------------------------------- hash | |
do | |
local function str_step(str, pos, rep, ...) | |
local chunk, rxlst = {}, {...} | |
pos, rep = pos or 1, rep or -1 | |
while rep ~= 0 do | |
local ps, pe | |
for k, rx in ipairs(rxlst) do | |
ps, pe = str:find('^'..rx, pos) | |
if ps then break end | |
end | |
if not ps then break end | |
insert(chunk, str:sub(ps, pe)) | |
log(2, 'str_step', ps, pe) | |
pos, rep = pe + 1, rep - 1 | |
end | |
return pos, chunk | |
end | |
local function array_insert(hash, chunk) | |
local p, c = str_step(hash, 1, nil, '%s+', '//.-\n', '/*.-*/') | |
p, c = str_step(hash, p, 1, '{') | |
if #c ~= 1 then return end | |
p, c = str_step(hash, p, 1, '[^%S\n]+') | |
return hash:sub(1, p - 1)..'\n'..chunk.. | |
hash:sub(p + ((hash:sub(p, p) == '\n') and 1 or 0)) | |
end | |
local function new_hash_entries(cfg) | |
local chunk = {} | |
insert(chunk, '//------ stupdmap BEGIN\n') | |
local fmt = ' "%s": <Single: {"src-dist": "%s"}>,\n' | |
for _, e in ipairs(cfg) do | |
insert(chunk, fmt:format(e.name, e.file)) | |
end | |
insert(chunk, '//------ stupdmap END\n') | |
return concat(chunk) | |
end | |
function compose_hash(cfg) | |
local fhash = mkpath(hash_dir, 'fonts.satysfi-hash') | |
local phash = sure(find_file(fhash, sys_root), | |
"system hash file is not found", fhash) | |
local hash = read_whole(phash) | |
hash = sure(array_insert(hash, new_hash_entries(cfg)), | |
"bad hash data") | |
log(3, "composed hash data", "========\n"..hash.."========") | |
mkdir(user_root[1], hash_dir) | |
write_whole(mkpath(user_root[1], fhash), hash) | |
end | |
end | |
---------------------------------------- logging | |
do | |
function log(level, ...) | |
if verbose < level then return end | |
io.stderr:write(concat({prog_name, ...}, ": ").."\n") | |
end | |
function info(...) log(0, ...) end | |
function warn(...) log(-1, "warning", ...) end | |
function abort(...) log(-2, ...); os.exit(1) end | |
function sure(val, a1, ...) | |
if val then return val end | |
abort((type(a1) == "number") and ("ERROR("..a1..")") or a1, ...) | |
end | |
end | |
---------------------------------------- main | |
do | |
local function show_usage() | |
io.stdout:write(([[ | |
This is %s, version %s <%s> | |
Usage: %s[.lua] [OPTION...] [CONF_FILE] | |
When CONF_FILE is omitted, then '%s' in the user library | |
root is assumed. | |
Options: | |
-h --help show this help | |
-V --version show the version | |
-q --quiet show less information | |
-v --verbose show more information | |
-d --dry-run dry run | |
-L --no-locate do not locate font files | |
-s --symlink use symbolic links to locate font files | |
]]):format(prog_name, version, mod_date, prog_name, default_cfg_file)) | |
os.exit(0) | |
end | |
local function show_version() | |
io.stdout:write(([[ | |
%s version %s | |
]]):format(prog_name, version)) | |
os.exit(0) | |
end | |
local function read_option() | |
local idx = 1 | |
while idx <= #arg do | |
if arg[idx]:sub(1, 1) ~= '-' then break end | |
local opt = arg[idx]; idx = idx + 1 | |
if opt == '-h' or opt == '--help' then | |
show_usage() | |
elseif opt == '-V' or opt == '--version' then | |
show_version() | |
elseif opt == '-q' or opt == '--quiet' then | |
verbose = -1 | |
elseif opt == '--verbose' then | |
verbose = 1 | |
elseif opt:match("^-v+$") then | |
verbose = #opt - 1 | |
elseif opt == '-d' or opt == '--dry-run' then | |
dry_run = true | |
elseif opt == '-L' or opt == '--no-locate' then | |
locate = false | |
elseif opt == '-s' or opt == '--symlink' then | |
if is_win then | |
warn("'-s' is not supported on Windows") | |
else | |
symlink = true | |
end | |
else abort("unknown option", opt) | |
end | |
end | |
cfg_file = arg[idx] | |
end | |
function main() | |
read_option() | |
log(1, 'is_win', is_win) | |
sure(user_root[1], 'user library root is unknown') | |
sure(is_dir(user_root[1]), | |
'user library root does not exist', user_root[1]) | |
local cfg_path = get_cfg_path() | |
local cfg_data = read_cfg_file(cfg_path) | |
resolve_location(cfg_data) | |
compose_hash(cfg_data) | |
end | |
end | |
---------------------------------------- done | |
main() | |
-- EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment