diff options
Diffstat (limited to 'luaotfload-tool.lua')
-rwxr-xr-x | luaotfload-tool.lua | 637 |
1 files changed, 32 insertions, 605 deletions
diff --git a/luaotfload-tool.lua b/luaotfload-tool.lua index cd64a75..92483d6 100755 --- a/luaotfload-tool.lua +++ b/luaotfload-tool.lua @@ -4,11 +4,13 @@ -- DESCRIPTION: database functionality -- REQUIREMENTS: luaotfload 2.2 -- AUTHOR: Khaled Hosny, Élie Roux, Philipp Gesang --- VERSION: 2.3b +-- VERSION: 2.4 -- LICENSE: GPL v2 --- MODIFIED: 2013-06-02 19:23:54+0200 +-- MODIFIED: 2013-07-28 13:12:04+0200 ----------------------------------------------------------------------- +local version = "2.4" --- <int: major>.<int: minor><alpha: fixes> + --[[doc-- This file was originally written (as \fileent{mkluatexfontdb.lua}) by @@ -42,23 +44,12 @@ kpse.set_program_name "luatex" local ioopen = io.open local iowrite = io.write local kpsefind_file = kpse.find_file -local kpseexpand_var = kpse.expand_var -local kpseexpand_path = kpse.expand_path -local lfsattributes = lfs.attributes -local lfsisfile = lfs.isfile -local lfsreadlink = lfs.readlink -local md5sumhexa = md5.sumhexa local next = next local osdate = os.date -local osgetenv = os.getenv -local osremove = os.remove -local osname = os.name -local ostype = os.type local stringexplode = string.explode local stringformat = string.format local stringlower = string.lower local stringrep = string.rep -local stringsub = string.sub local tableconcat = table.concat local texiowrite_nl = texio.write_nl local texiowrite = texio.write @@ -79,7 +70,7 @@ else -- 5.2 end -local C, Cg, Ct, P, S = lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S +local C, Ct, P, S = lpeg.C, lpeg.Ct, lpeg.P, lpeg.S local lpegmatch = lpeg.match local loader_file = "luatexbase.loader.lua" @@ -111,6 +102,7 @@ to be the more appropriate. config = config or { } local config = config config.luaotfload = config.luaotfload or { } +config.luaotfload.version = config.luaotfload.version or version config.luaotfload.names_dir = config.luaotfload.names_dir or "names" config.luaotfload.cache_dir = config.luaotfload.cache_dir or "fonts" config.luaotfload.index_file = config.luaotfload.index_file @@ -140,26 +132,33 @@ config.lualibs.prefer_merged = true config.lualibs.load_extended = true require "lualibs" - -local fileisreadable = file.isreadable -local fileiswritable = file.iswritable -local filesplitpath = file.splitpath -local ioloaddata = io.loaddata -local lua_of_json = utilities.json.tolua local tabletohash = table.tohash --[[doc-- \fileent{luatex-basics-gen.lua} calls functions from the \luafunction{texio.*} library; too much for our taste. We intercept them with dummies. + +Also, it sets up dummies in place of the tables created by the Context +libraries. Since we have loaded the lualibs already this would cause +collateral damage for some libraries whose namespace would be +overridden. We employ our usual backup-restore strategy to work around +this. (Postponing the loading of the lualibs code is not an option +because the functionality is needed by basics-gen itself.) --doc]]-- local dummy_function = function ( ) end -local backup_write, backup_write_nl = texio.write, texio.write_nl +local backup = { + write = texio.write, + write_nl = texio.write_nl, + utilities = utilities, +} texio.write, texio.write_nl = dummy_function, dummy_function require"luaotfload-basics-gen.lua" -texio.write, texio.write_nl = backup_write, backup_write_nl + +texio.write, texio.write_nl = backup.write, backup.write_nl +utilities = backup.utilities require"luaotfload-override.lua" --- this populates the logs.* namespace require"luaotfload-database" @@ -167,12 +166,11 @@ require"alt_getopt" local names = fonts.names -local status_file = "luaotfload-status" -local status = require (status_file) - -local version = "2.3b" +local status_file = "luaotfload-status" +local status = require (status_file) +config.luaotfload.status = status -local sanitize_string = names.sanitize_string +local sanitize_string = names.sanitize_string local pathdata = names.path local names_plain = pathdata.index.lua @@ -830,10 +828,6 @@ local get_fields get_fields = function (entry, fields, acc, n) return acc end -local comma = P"," -local noncomma = 1-comma -local split_comma = Ct((C(noncomma^1) + comma)^1) - local separator = "\t" --- could be “,” for csv local format_fields format_fields = function (fields, acc, n) @@ -869,12 +863,14 @@ set_primary_field = function (fields, addme, acc, n) return acc end +local splitcomma = names.patterns.splitcomma + actions.list = function (job) local criterion = job.criterion local asked_fields = job.asked_fields if asked_fields then - asked_fields = lpegmatch(split_comma, asked_fields) + asked_fields = lpegmatch(splitcomma, asked_fields) else --- some defaults asked_fields = { "fullname", "version", } @@ -959,579 +955,10 @@ actions.list = function (job) return true, true end -do - local out = function (...) - logs.names_report (false, 0, "diagnose", ...) - end - - local verify_files = function (errcnt, status) - out "================ verify files =================" - local hashes = status.hashes - local notes = status.notes - if not hashes or #hashes == 0 then - out ("FAILED: cannot read checksums from %s.", status_file) - return 1/0 - elseif not notes then - out ("FAILED: cannot read commit metadata from %s.", - status_file) - return 1/0 - end - - out ("Luaotfload revision %s.", notes.revision) - out ("Committed by %s.", notes.committer) - out ("Timestamp %s.", notes.timestamp) - - local nhashes = #hashes - out ("Testing %d files for integrity.", nhashes) - for i = 1, nhashes do - local fname, canonicalsum = unpack (hashes[i]) - local location = kpsefind_file (fname) - or kpsefind_file (fname, "texmfscripts") - if not location then - errcnt = errcnt + 1 - out ("FAILED: file %s missing.", fname) - else - out ("File: %s.", location) - local raw = ioloaddata (location) - if not raw then - errcnt = errcnt + 1 - out ("FAILED: file %d not readable.", fname) - else - local sum = md5sumhexa (raw) - if sum ~= canonicalsum then - errcnt = errcnt + 1 - out ("FAILED: checksum mismatch for file %s.", - fname) - out ("Expected %s.", canonicalsum) - out ("Got %s.", sum) - else - out ("Ok, %s passed.", fname) - end - end - end - end - return errcnt - end - - local get_tentative_attributes = function (file) - if not lfsisfile (file) then - local chan = ioopen (file, "w") - if chan then - chan:close () - local attributes = lfsattributes (file) - os.remove (file) - return attributes - end - end - end - - local p_permissions = Ct(Cg(Ct(C(1) * C(1) * C(1)), "u") - * Cg(Ct(C(1) * C(1) * C(1)), "g") - * Cg(Ct(C(1) * C(1) * C(1)), "o")) - - local analyze_permissions = function (raw) - return lpegmatch (p_permissions, raw) - end - - local stripslashes = names.patterns.stripslashes - - local get_permissions = function (t, location) - if stringsub (location, #location) == "/" then - --- strip trailing slashes (lfs idiosyncrasy on Win) - location = lpegmatch (stripslashes, location) - end - local attributes = lfsattributes (location) - - if not attributes and t == "f" then - attributes = get_tentative_attributes (location) - if not attributes then - return false - end - end - - local permissions - - if fileisreadable (location) then - --- link handling appears to be unnecessary because - --- lfs.attributes() will return the information on - --- the link target. - if mode == "link" then --follow and repeat - location = lfsreadlink (location) - attributes = lfsattributes (location) - end - end - - permissions = analyze_permissions (attributes.permissions) - - return { - location = location, - mode = attributes.mode, - owner = attributes.uid, --- useless on windows - permissions = permissions, - attributes = attributes, - } - end - - local check_conformance = function (spec, permissions, errcnt) - local uid = permissions.attributes.uid - local gid = permissions.attributes.gid - local raw = permissions.attributes.permissions - - out ("Owner: %d, group %d, permissions %s.", uid, gid, raw) - if ostype == "unix" then - if uid == 0 or gid == 0 then - out "Owned by the superuser, permission conflict likely." - errcnt = errcnt + 1 - end - end - - local user = permissions.permissions.u - if spec.r == true then - if user[1] == "r" then - out "Readable: ok." - else - out "Not readable: permissions need fixing." - errcnt = errcnt + 1 - end - end - - if spec.w == true then - if user[2] == "w" - or fileiswritable (permissions.location) then - out "Writable: ok." - else - out "Not writable: permissions need fixing." - errcnt = errcnt + 1 - end - end - - return errcnt - end - - local path = names.path - - local desired_permissions = { - { "d", {"r","w"}, function () return caches.getwritablepath () end }, - { "d", {"r","w"}, path.globals.prefix }, - { "f", {"r","w"}, path.index.lua }, - { "f", {"r","w"}, path.index.luc }, - { "f", {"r","w"}, path.lookups.lua }, - { "f", {"r","w"}, path.lookups.luc }, - } - - local check_permissions = function (errcnt) - out [[=============== file permissions ==============]] - for i = 1, #desired_permissions do - local t, spec, path = unpack (desired_permissions[i]) - if type (path) == "function" then - path = path () - end - - spec = tabletohash (spec) - - out ("Checking permissions of %s.", path) - - local permissions = get_permissions (t, path) - if permissions then - --inspect (permissions) - errcnt = check_conformance (spec, permissions, errcnt) - else - errcnt = errcnt + 1 - end - end - return errcnt - end - - local check_upstream - - if kpsefind_file ("https.lua", "lua") == nil then - check_upstream = function (errcnt) - out [[============= upstream repository ============= - WARNING: Cannot retrieve repository data. - Github API access requires the luasec library. - Grab it from <https://github.com/brunoos/luasec> - and retry.]] - return errcnt - end - else - --- github api stuff begin - local https = require "ssl.https" - - local gh_api_root = [[https://api.github.com]] - local release_url = [[https://github.com/lualatex/luaotfload/releases]] - local luaotfload_repo = [[lualatex/luaotfload]] - local user_agent = [[lualatex/luaotfload integrity check]] - local shortbytes = 8 - - local gh_shortrevision = function (rev) - return stringsub (rev, 1, shortbytes) - end - - local gh_encode_parameters = function (parameters) - local acc = {} - for field, value in next, parameters do - --- unsafe, non-urlencoded coz it’s all ascii chars - acc[#acc+1] = field .. "=" .. value - end - return "?" .. tableconcat (acc, "&") - end - - local gh_make_url = function (components, parameters) - local url = tableconcat ({ gh_api_root, - unpack (components) }, - "/") - if parameters then - url = url .. gh_encode_parameters (parameters) - end - return url - end - - local alright = [[HTTP/1.1 200 OK]] - - local gh_api_request = function (...) - local args = {...} - local nargs = #args - local final = args[nargs] - local request = { - url = "", - headers = { ["user-agent"] = user_agent }, - } - if type (final) == "table" then - args[nargs] = nil - request = gh_make_url (args, final) - else - request = gh_make_url (args) - end - - out ("Requesting <%s>.", request) - local response, code, headers, status - = https.request (request) - if status ~= alright then - out "Request failed!" - return false - end - return response - end - - local gh_api_checklimit = function (headers) - local rawlimit = gh_api_request "rate_limit" - local limitdata = lua_of_json (rawlimit) - if not limitdata and limitdata.rate then - out "Cannot parse API rate limit." - return false - end - limitdata = limitdata.rate - - local limit = tonumber (limitdata.limit) - local left = tonumber (limitdata.remaining) - local reset = tonumber (limitdata.reset) - - out ("%d of %d Github API requests left.", left, limit) - if left == 0 then - out ("Cannot make any more API requests.") - out ("Try again later at %s.", osdate ("%F %T", reset)) - end - return true - end - - local gh_tags = function () - out "Fetching tags from repository, please stand by." - local rawtags = gh_api_request ("repos", - luaotfload_repo, - "tags") - local taglist = lua_of_json (rawtags) - if not taglist or #taglist == 0 then - out "Cannot parse response." - return false - end - - local ntags = #taglist - out ("Repository contains %d tags.", ntags) - local _idx, latest = next (taglist) - out ("The most recent release is %s (revision %s).", - latest.name, - gh_shortrevision (latest.commit.sha)) - return latest - end - - local gh_compare = function (head, base) - if base == nil then - base = "HEAD" - end - out ("Fetching comparison between %s and %s, \z - please stand by.", - gh_shortrevision (head), - gh_shortrevision (base)) - local comparison = base .. "..." .. head - local rawstatus = gh_api_request ("repos", - luaotfload_repo, - "compare", - comparison) - local status = lua_of_json (rawstatus) - if not status then - out "Cannot parse response for status request." - return false - end - return status - end - - local gh_news = function (since) - local compared = gh_compare (since) - if not compared then - return false - end - local behind_by = compared.behind_by - local ahead_by = compared.ahead_by - local status = compared.status - out ("Comparison state: %s.", status) - if behind_by > 0 then - out ("Your Luaotfload is %d \z - revisions behind upstream.", - behind_by) - return behind_by - elseif status == "ahead" then - out "Since you are obviously from the future \z - I assume you already know the repository state." - else - out "Everything up to date. \z - Luaotfload is in sync with upstream." - end - return false - end - - local gh_catchup = function (current, latest) - local compared = gh_compare (latest, current) - local ahead_by = tonumber (compared.ahead_by) - if ahead_by > 0 then - local permalink_url = compared.permalink_url - out ("Your Luaotfload is %d revisions \z - behind the most recent release.", - ahead_by) - out ("To view the commit log, visit <%s>.", - permalink_url) - out ("You can grab an up to date tarball at <%s>.", - release_url) - return true - else - out "There weren't any new releases in the meantime." - out "Luaotfload is up to date." - end - return false - end - - check_upstream = function (current) - out "============= upstream repository =============" - local _succ = gh_api_checklimit () - local behind = gh_news (current) - if behind then - local latest = gh_tags () - local _behind = gh_catchup (current, - latest.commit.sha, - latest.name) - end - end - - --- trivium: diff since the first revision as pushed by Élie - --- in 2009 - --- local firstrevision = "c3ccb3ee07e0a67171c24960966ae974e0dd8e98" - --- check_upstream (firstrevision) - end - --- github api stuff end - - local print_envvar = function (var) - local val = osgetenv (var) - if val then - out ("%20s: %q", stringformat ("$%s", var), val) - return val - else - out ("%20s: <unset>", stringformat ("$%s", var)) - end - end - - local print_path = function (var) - local val = osgetenv (var) - if val then - local paths = filesplitpath (val) - if paths then - local npaths = #paths - if npaths == 1 then - out ("%20s: %q", stringformat ("$%s", var), val) - elseif npaths > 1 then - out ("%20s: <%d items>", stringformat ("$%s", var), npaths) - for i = 1, npaths do - out (" +: %q", paths[i]) - end - else - out ("%20s: <empty>") - end - end - else - out ("%20s: <unset>", stringformat ("$%s", var)) - end - end - - local print_kpsevar = function (var) - var = "$" .. var - local val = kpseexpand_var (var) - if val and val ~= var then - out ("%20s: %q", var, val) - return val - else - out ("%20s: <empty or unset>", var) - end - end - - local print_kpsepath = function (var) - var = "$" .. var - local val = kpseexpand_path (var) - if val and val ~= "" then - local paths = filesplitpath (val) - if paths then - local npaths = #paths - if npaths == 1 then - out ("%20s: %q", var, paths[1]) - elseif npaths > 1 then - out ("%20s: <%d items>", var, npaths) - for i = 1, npaths do - out (" +: %q", paths[i]) - end - else - out ("%20s: <empty>") - end - end - else - out ("%20s: <empty or unset>", var) - end - end - - --- this test first if a variable is set and then expands the - --- paths; this is necessitated by the fact that expand-path will - --- return the empty string both if the variable is unset and if - --- the directory does not exist - - local print_kpsepathvar = function (var) - local vvar = "$" .. var - local val = kpseexpand_var (vvar) - if val and val ~= vvar then - out ("%20s: %q", vvar, val) - print_kpsepath (var) - else - out ("%20s: <empty or unset>", var) - end - end - - local check_environment = function (errcnt) - out "============ environment settings =============" - out ("system: %s/%s", ostype, osname) - if ostype == "unix" and io.popen then - local chan = io.popen ("uname -a", "r") - if chan then - out ("info: %s", chan:read "*all") - chan:close () - end - end - - out "1) *shell environment*" - print_envvar "SHELL" - print_path "PATH" - print_path "OSFONTDIR" - print_envvar "USER" - if ostype == "windows" then - print_envvar "WINDIR" - print_envvar "CD" - print_path "TEMP" - elseif ostype == "unix" then - print_envvar "HOME" - print_envvar "PWD" - print_path "TMPDIR" - end - - out "2) *kpathsea*" - print_kpsepathvar "OPENTYPEFONTS" - print_kpsepathvar "TTFONTS" - - print_kpsepathvar "TEXMFCACHE" - print_kpsepathvar "TEXMFVAR" - - --- the expansion of these can be quite large; as they aren’t - --- usually essential to luaotfload, we won’t dump every single - --- path - print_kpsevar "LUAINPUTS" - print_kpsevar "CLUAINPUTS" - - return errcnt - end - - local anamneses = { - "environment", - "files", - "repository", - "permissions" - } - - actions.diagnose = function (job) - local errcnt = 0 - local asked = job.asked_diagnostics - if asked == "all" or asked == "thorough" then - asked = tabletohash (anamneses, true) - else - asked = lpegmatch(split_comma, asked) - asked = tabletohash (asked, true) - end - - if asked.environment == true then - errcnt = check_environment (errcnt) - asked.environment = nil - end - - if asked.files == true then - errcnt = verify_files (errcnt, status) - asked.files = nil - end - - if asked.permissions == true then - errcnt = check_permissions (errcnt) - asked.permissions = nil - end - - if asked.repository == true then - check_upstream (status.notes.revision) - asked.repository = nil - end - - local rest = next (asked) - if rest ~= nil then --> something unknown - out ("Unknown diagnostic %q.", rest) - end - if errcnt == 0 then --> success - out ("Everything appears to be in order, \z - you may sleep well.") - return true, false - end - out ( [[=============================================== - WARNING - =============================================== - - The diagnostic detected %d errors. - - This version of luaotfload may have been - tampered with. Modified versions of the - luaotfload source are unsupported. Read the log - carefully and get a clean version from CTAN or - github: - - × http://ctan.org/tex-archive/macros/luatex/generic/luaotfload - × https://github.com/lualatex/luaotfload/releases - - If you are uncertain as to how to proceed, then - ask on the lualatex mailing list: - - http://www.tug.org/mailman/listinfo/lualatex-dev - - =============================================== -]], errcnt) - return true, false - end +actions.diagnose = function (job) + --- diagnostics are loaded on demand + local diagnose = require "luaotfload-diagnostics.lua" + return diagnose (job) end --- stuff to be carried out prior to exit |