summaryrefslogtreecommitdiff
path: root/support/texfindpkg/texfindpkg.lua
diff options
context:
space:
mode:
Diffstat (limited to 'support/texfindpkg/texfindpkg.lua')
-rw-r--r--support/texfindpkg/texfindpkg.lua587
1 files changed, 348 insertions, 239 deletions
diff --git a/support/texfindpkg/texfindpkg.lua b/support/texfindpkg/texfindpkg.lua
index f2845f9d90..160684474b 100644
--- a/support/texfindpkg/texfindpkg.lua
+++ b/support/texfindpkg/texfindpkg.lua
@@ -5,16 +5,29 @@
-- Repository: https://github.com/lvjr/texfindpkg
-- License: GNU General Public License v3.0
-local tfpversion = "2023D"
-local tfpdate = "2023-04-05"
+local tfp = tfp or {}
+
+tfp.version = "2023E"
+tfp.date = "2023-05-05"
+
+local building = tfp.building
+local tfpresult = ""
------------------------------------------------------------
--> \section{Some variables and functions}
------------------------------------------------------------
local lfs = require("lfs")
+
local insert = table.insert
-local match = string.match
+local remove = table.remove
+local concat = table.concat
+local gmatch = string.gmatch
+local match = string.match
+local find = string.find
+local gsub = string.gsub
+local sub = string.sub
+local rep = string.rep
local lookup = kpse.lookup
kpse.set_program_name("kpsewhich")
@@ -24,7 +37,18 @@ local json = utilities.json -- for json.tostring and json.tolua
local gzip = gzip -- for gzip.compress and gzip.decompress
local function tfpPrint(msg)
- print("[tfp] " .. msg)
+ msg = "[tfp] " .. msg
+ if building then
+ tfpresult = tfpresult .. msg .. "\n"
+ else
+ print(msg)
+ end
+end
+
+local function tfpRealPrint(msg)
+ if not building then
+ print("[tfp] " .. msg)
+ end
end
local showdbg = false
@@ -85,6 +109,7 @@ end
------------------------------------------------------------
local tlpkgtext
+local tlinspkgtext
local function tlReadPackageDB()
local tlroot = kpse.var_value("TEXMFROOT")
@@ -97,36 +122,52 @@ local function tlReadPackageDB()
if #list > 0 then
tlpkgtext = fileRead(tlroot .. "/" .. list[1])
if not tlpkgtext then
- tfpPrint("error in reading texlive package database!")
+ tfpPrint("error in reading texlive.tlpdb.main file!")
end
else
-- no texlive.tlpdb.main file in a fresh TeX live
tfpPrint("error in finding texlive package database!")
tfpPrint("please run 'tlmgr update --self' first.")
end
+ tlinspkgtext = fileRead(tlroot .. "/texlive.tlpdb")
+ if not tlinspkgtext then
+ tfpPrint("error in reading texlive.tlpdb file!")
+ end
end
local tlpkgdata = {}
+local tlinspkgdata = {}
local function tlExtractFiles(name, desc)
-- ignore binary packages
-- also ignore latex-dev packages
- if name:find("%.") or name:find("^latex%-[%a]-%-dev") then
+ if find(name, "%.") or find(name, "^latex%-[%a]-%-dev") then
--print(name)
return
end
-- ignore package files in doc folder
- desc = desc:match("\nrunfiles .+") or ""
- for base, ext in desc:gmatch("/([%a%d%-]+)%.([%a%d]+)\n") do
- if ext == "sty" or ext == "cls" then
+ desc = match(desc, "\nrunfiles .+") or ""
+ for base, ext in gmatch(desc, "/([%a%d%-%.]+)%.([%a%d]+)\n") do
+ if ext == "sty" or ext == "cls" or ext == "tex" or ext == "ltx" then
dbgPrint(name, base .. "." .. ext)
tlpkgdata[base .. "." .. ext] = name
end
end
end
-local function tlParsePackageDB()
- tlpkgtext:gsub("name (.-)\n(.-)\n\n", tlExtractFiles)
+local function tlExtractPackages(name, desc)
+ tlinspkgdata[name] = true
+end
+
+local function tlParsePackageDB(tlpkgtext)
+ gsub(tlpkgtext, "name (.-)\n(.-)\n\n", tlExtractFiles)
+ return tlpkgdata
+end
+
+local function tlParseTwoPackageDB()
+ gsub(tlpkgtext, "name (.-)\n(.-)\n\n", tlExtractFiles)
+ -- texlive.tlpdb might use different eol characters
+ gsub(tlinspkgtext, "name (.-)\r?\n(.-)\r?\n\r?\n", tlExtractPackages)
end
------------------------------------------------------------
@@ -134,186 +175,75 @@ end
------------------------------------------------------------
local mtpkgtext
+local mtinspkgtext
local function mtReadPackageDB()
- local mtvar = kpse.var_value("TEXMFVAR")
+ local mtvar = kpse.var_value("TEXMFDIST")
if mtvar then
- mtpdb = mtvar .. "/miktex/cache/packages/miktex-zzdb3-2.9/package-manifests.ini"
+ mtpkgtext = fileRead(mtvar .. "/miktex/config/package-manifests.ini")
+ if not mtpkgtext then
+ tfpPrint("error in reading package-manifests.ini file!")
+ end
+ mtinspkgtext = fileRead(mtvar .. "/miktex/config/packages.ini")
+ if not mtinspkgtext then
+ tfpPrint("error in reading packages.ini file!")
+ end
else
tfpPrint("error in finding texmf root!")
end
- mtpkgtext = fileRead(mtpdb)
- if not mtpkgtext then
- tfpPrint("error in reading miktex package database!")
- end
end
local mtpkgdata = {}
+local mtinspkgdata = {}
local function mtExtractFiles(name, desc)
-- ignore package files in source or doc folders
-- also ignore latex-dev packages
- if name:find("_") or name:find("^latex%-[%a]-%-dev") then
+ if find(name, "_") or find(name, "^latex%-[%a]-%-dev") then
--print(name)
return
end
- for base, ext in desc:gmatch("/([%a%d%-]+)%.([%a%d]+)\r?\n") do
- if ext == "sty" or ext == "cls" then
+ for base, ext in gmatch(desc, "/([%a%d%-%.]+)%.([%a%d]+)\r?\n") do
+ if ext == "sty" or ext == "cls" or ext == "tex" or ext == "ltx" then
dbgPrint(name, base .. "." .. ext)
mtpkgdata[base .. "." .. ext] = name
end
end
end
-local function mtParsePackageDB()
- -- package-manifests.ini might use different eol characters
- mtpkgtext:gsub("%[(.-)%]\r?\n(.-)\r?\n\r?\n", mtExtractFiles)
-end
-
-------------------------------------------------------------
---> \section{Compare TeX Live and MiKTeX packages}
-------------------------------------------------------------
-
-local tlpkgname = "download/texlive.tlpdb"
-local mtpkgname = "download/package-manifests.ini"
-
-local function compareDistributions()
- tlpkgtext = fileRead(tlpkgname)
- if tlpkgtext then
- tlParsePackageDB()
- else
- tfpPrint("error in reading texlive package database!")
- end
- mtpkgtext = fileRead(mtpkgname)
- if mtpkgtext then
- mtParsePackageDB()
- else
- tfpPrint("error in reading miktex package database!")
- end
- local tlmissing, mkmissing = {}, {}
- for k, vt in pairs(tlpkgdata) do
- local vm = mtpkgdata[k] -- or "[none]"
- if vm then
- mtpkgdata[k] = nil -- remove it
- if vm ~= vt then
- print("texlive->" .. vt, "miktex->" .. vm, k)
- end
- else
- insert(mkmissing, {vt, k})
- end
- end
- for k, v in pairs(mtpkgdata) do
- print("texlive doesn't include " .. v .. " -> " .. k)
- end
- for _, v in ipairs(mkmissing) do
- print("miktex doesn't include " .. v[1] .. " -> " .. v[2])
- end
-end
-
-------------------------------------------------------------
---> \section{Generate json file from cwl files}
-------------------------------------------------------------
-
-local function insertNewValue(tbl, val)
- if not valueExists(tbl, val) then
- insert(tbl, val)
- end
-end
-
-local function extractFileData(cwl)
- -- the cwl files have different eol characters
- local deps = {}
- for d in cwl:gmatch("\n#include:(.-)[\r\n]") do
- --dbgPrint(d)
- n = d:match("^class-(.+)$")
- if n then
- insertNewValue(deps, n .. ".cls")
- else
- insertNewValue(deps, d .. ".sty")
- end
- end
- local envs = {}
- for b, e in cwl:gmatch("\n\\begin{(.-)}.-\n\\end{(.-)}") do
- if b == e then
- --dbgPrint("{" .. e .. "}")
- insert(envs, e)
- end
- end
- local cmds = {}
- for c in cwl:gmatch("\n\\(%a+)") do
- if c ~= "begin" and c ~= "end" then
- --dbgPrint("\\" .. c)
- if not valueExists(cmds, c) then
- insert(cmds, c)
- end
- end
- end
- --return {deps, envs, cmds}
- return {deps = deps, envs = envs, cmds = cmds}
+local function mtExtractPackages(name, desc)
+ mtinspkgdata[name] = true
end
-local function writeJson(cwldata)
- tfpPrint("writing json database to file...")
- local tbl1 = {}
- for k, v in pairs(cwldata) do
- table.insert(tbl1, {k, v})
- end
- table.sort(tbl1, function(a, b)
- if a[1] < b[1] then return true end
- end)
- local tbl2 = {}
- for _, v in ipairs(tbl1) do
- local item = '"' .. v[1] .. '":' .. json.tostring(v[2])
- table.insert(tbl2, item)
- end
- local text = "{\n" .. table.concat(tbl2, "\n,\n") .. "\n}"
- fileWrite(text, "texfindpkg.json")
- fileWrite(gzip.compress(text), "texfindpkg.json.gz")
+local function mtParsePackageDB(mtpkgtext)
+ -- package-manifests.ini might use different eol characters
+ gsub(mtpkgtext, "%[(.-)%]\r?\n(.-)\r?\n\r?\n", mtExtractFiles)
+ return mtpkgdata
end
-local cwlpath = "completion"
-
-local function generateJsonData()
- local list = getFiles(cwlpath, "%.cwl$")
- local fname
- local cwldata = {}
- for _, v in ipairs(list) do
- local a, b = match(v, "^([^%-]-)%-(.+)%.cwl")
- if a == "class" then
- fname = b .. ".cls"
- else
- b = match(v, "^(.+)%.cwl")
- fname = b .. ".sty"
- end
- --print(fname)
- local cwl = fileRead(cwlpath .. "/" .. v)
- --print(cwl)
- if cwl then
- local item = extractFileData(cwl)
- dbgPrint(item)
- cwldata[fname] = item
- else
- tfpPrint("error in reading " .. v)
- end
- end
- writeJson(cwldata)
+local function mtParseTwoPackageDB()
+ -- package-manifests.ini and packages.ini might use different eol characters
+ gsub(mtpkgtext, "%[(.-)%]\r?\n(.-)\r?\n\r?\n", mtExtractFiles)
+ gsub(mtinspkgtext, "%[(.-)%]\r?\n(.-)\r?\n\r?\n", mtExtractPackages)
end
------------------------------------------------------------
--> \section{Install packages in current TeX distribution}
------------------------------------------------------------
-local dist -- name of current tex distribution
+local dist -- name of current tex distribution
+local totalinslist = {} -- list of all missing packages
+local filecount = 0 -- total number of files found
local function initPackageDB()
dist = testDistribution()
tfpPrint("you are using " .. dist)
if dist == "texlive" then
tlReadPackageDB()
- tlParsePackageDB()
+ tlParseTwoPackageDB()
else
mtReadPackageDB()
- mtParsePackageDB()
+ mtParseTwoPackageDB()
end
end
@@ -325,32 +255,86 @@ local function findOnePackage(fname)
end
end
-local function tfpExecute(c)
- if os.type == "windows" then
- os.execute(c)
+local function checkInsPakage(pkg)
+ if dist == "texlive" then
+ return tlinspkgdata[pkg]
else
- os.execute('sudo env "PATH=$PATH" ' .. c)
+ return mtinspkgdata[pkg]
+ end
+end
+
+local function tfpExecute(c)
+ if not building then
+ if os.type == "windows" then
+ os.execute(c)
+ else
+ os.execute('sudo env "PATH=$PATH" ' .. c)
+ end
end
end
local function installSomePackages(list)
if not list then return end
if dist == "texlive" then
- local p = table.concat(list, " ")
- tfpPrint("installing package " .. p)
- tfpExecute("tlmgr install " .. p)
+ local pkgs = concat(list, " ")
+ if #list > 1 then
+ tfpRealPrint("installing texlive packages: " .. pkgs)
+ else
+ tfpRealPrint("installing texlive package: " .. pkgs)
+ end
+ tfpExecute("tlmgr install " .. pkgs)
else
for _, p in ipairs(list) do
- tfpPrint("installing package " .. p)
+ tfpRealPrint("installing miktex package: " .. p)
tfpExecute("miktex packages install " .. p)
end
end
end
+local function updateTotalInsList(inslist)
+ if #totalinslist == 0 then
+ totalinslist = inslist
+ else
+ for _, pkg in ipairs(inslist) do
+ if not valueExists(totalinslist, pkg) then
+ insert(totalinslist, pkg)
+ end
+ end
+ end
+end
+
local function listSomePackages(list)
- if not list then return end
- local p = table.concat(list, " ")
- tfpPrint("please install " .. dist .. " package " .. p)
+ if not list then return {} end
+ if #list > 0 then
+ filecount = filecount + 1
+ end
+ local pkgs = concat(list, " ")
+ if #list == 1 then
+ tfpPrint(dist .. " package needed: " .. pkgs)
+ else
+ tfpPrint(dist .. " packages needed: " .. pkgs)
+ end
+ local inslist = {}
+ for _, p in ipairs(list) do
+ if not checkInsPakage(p) then
+ insert(inslist, p)
+ end
+ end
+ if #inslist == 0 then
+ if #list == 1 then
+ tfpRealPrint("this package is already installed")
+ else
+ tfpRealPrint("these packages are already installed")
+ end
+ else
+ local pkgs = concat(inslist, " ")
+ if #inslist == 1 then
+ tfpRealPrint(dist .. " package not yet installed: " .. pkgs)
+ else
+ tfpRealPrint(dist .. " packages not yet installed: " .. pkgs)
+ end
+ end
+ updateTotalInsList(inslist)
end
------------------------------------------------------------
@@ -359,7 +343,8 @@ end
local tfptext = "" -- the json text
local tfpdata = {} -- the lua object
-local fnlist = {} -- file name list
+local fnlist = {} -- file name list
+local pkglist = {} -- package name list
local function initDependencyDB()
local ziptext = fileRead(lookup("texfindpkg.json.gz"))
@@ -372,67 +357,87 @@ local function initDependencyDB()
end
end
-local function findDependencies(fname)
+local function printDependency(fname, level)
+ local msg = fname
+ local pkg = findOnePackage(fname)
+ if pkg then
+ msg = msg .. " (from " .. pkg .. ")"
+ if not valueExists(pkglist, pkg) then
+ insert(pkglist, pkg)
+ end
+ else
+ msg = msg .. " (not found)"
+ end
+ if level == 0 then
+ tfpPrint(msg)
+ else
+ tfpPrint(rep(" ", level - 1) .. "|- " .. msg)
+ end
+end
+
+local function findDependencies(fname, level)
--print(fname)
if valueExists(fnlist, fname) then return end
local item = tfpdata[fname]
if not item then
- tfpPrint("no dependency info for " .. fname)
+ -- no dependency info for fname
+ printDependency(fname, level)
return
end
- tfpPrint("finding dependencies for " .. fname)
- table.insert(fnlist, fname)
+ -- finding dependencies for fname
+ printDependency(fname, level)
+ insert(fnlist, fname)
local deps = item.deps
if deps then
for _, dname in ipairs(deps) do
- findDependencies(dname)
+ findDependencies(dname, level + 1)
end
end
end
local function queryByFileName(fname)
- fnlist = {} -- reset the list
- findDependencies(fname)
+ fnlist, pkglist = {}, {} -- reset the list
+ tfpPrint("building dependency tree for " .. fname .. ":")
+ tfpPrint(rep("-", 24))
+ findDependencies(fname, 0)
+ tfpPrint(rep("-", 24))
if #fnlist == 0 then
tfpPrint("could not find any package with file " .. fname)
return
end
- local pkglist = {}
- for _, fn in ipairs(fnlist) do
- local pkg = findOnePackage(fn)
- --print(fn, pkg)
- if pkg then
- table.insert(pkglist, pkg)
- end
- end
- if not pkglist then
+ if #pkglist == 0 then
tfpPrint("error in finding package in " .. dist)
return
end
- return pkglist
+ listSomePackages(pkglist)
end
local function getFileNameFromCmdEnvName(cmdenv, name)
--print(name)
- for line in tfptext:gmatch("(.-)\n[,}]") do
- if line:find('"' .. name .. '"') then
+ local flist = {}
+ for line in gmatch(tfptext, "(.-)\n[,}]") do
+ if find(line, '"' .. name .. '"') then
--print(line)
- local fname, fspec = line:match('"(.-)":(.+)')
+ local fname, fspec = match(line, '"(.-)":(.+)')
--print(fname, fspec)
local item = json.tolua(fspec)
- if valueExists(item[cmdenv], name) then
- tfpPrint("found package file " .. fname)
- return fname
+ if item[cmdenv] and valueExists(item[cmdenv], name) then
+ insert(flist, fname)
end
end
end
+ return flist
end
local function queryByCommandName(cname)
--print(cname)
- local fname = getFileNameFromCmdEnvName("cmds", cname)
- if fname then
- return queryByFileName(fname)
+ local flist = getFileNameFromCmdEnvName("cmds", cname)
+ if #flist > 0 then
+ for _, fname in ipairs(flist) do
+ tfpPrint(rep("=", 48))
+ tfpPrint("found package file " .. fname .. " with command \\" .. cname)
+ queryByFileName(fname)
+ end
else
tfpPrint("could not find any package with command \\" .. cname)
end
@@ -440,83 +445,187 @@ end
local function queryByEnvironmentName(ename)
--print(ename)
- local fname = getFileNameFromCmdEnvName("envs", ename)
- if fname then
- return queryByFileName(fname)
+ local flist = getFileNameFromCmdEnvName("envs", ename)
+ if #flist > 0 then
+ for _, fname in ipairs(flist) do
+ tfpPrint(rep("=", 48))
+ tfpPrint("found package file " .. fname .. " with environment {" .. ename .. "}")
+ queryByFileName(fname)
+ end
else
tfpPrint("could not find any package with environment {" .. ename .. "}")
end
end
-local function query(name)
- local h = name:sub(1,1)
+local function queryOne(t, name)
+ if t == "cmd" then
+ queryByCommandName(name)
+ elseif t == "env" then
+ queryByEnvironmentName(name)
+ elseif t == "file" then
+ tfpPrint(rep("=", 48))
+ queryByFileName(name)
+ else
+ -- do something for t == "pkg"
+ end
+end
+
+local function query(namelist)
+ for _, v in ipairs(namelist) do
+ queryOne(v[1], v[2])
+ end
+ if filecount > 1 then
+ tfpRealPrint(rep("=", 48))
+ local pkgs = concat(totalinslist, " ")
+ if #totalinslist == 1 then
+ tfpRealPrint(dist .. " package not yet installed in total: " .. pkgs)
+ else
+ tfpRealPrint(dist .. " packages not yet installed in total: " .. pkgs)
+ end
+ end
+end
+
+local function install(namelist)
+ query(namelist)
+ if #totalinslist > 0 then
+ installSomePackages(totalinslist)
+ end
+end
+
+------------------------------------------------------------
+--> \section{Parse query or install arguments}
+------------------------------------------------------------
+
+local function parseName(name)
+ local h = sub(name, 1, 1)
if h == "\\" then
- local b = name:sub(2)
- return queryByCommandName(b)
+ local b = sub(name, 2)
+ return({"cmd", b})
elseif h == "{" then
- if name:sub(-1) == "}" then
- local b = name:sub(2,-2)
- return queryByEnvironmentName(b)
+ if sub(name, -1) == "}" then
+ local b = sub(name, 2, -2)
+ return({"env", b})
+ else
+ error("invalid name '" .. name .. "'")
+ end
+ elseif find(name, "%.") then
+ return({"file", name})
+ else
+ return({"pkg", name})
+ end
+end
+
+local function parseArgList(arglist)
+ local namelist = {}
+ local nametype = nil
+ for _, v in ipairs(arglist) do
+ if v == "-f" then
+ nametype = "file"
+ elseif v == "-c" then
+ nametype = "cmd"
+ elseif v == "-e" then
+ nametype = "env"
else
- tfpPrint("invalid input " .. name)
+ if nametype then
+ insert(namelist, {nametype, v})
+ else
+ insert(namelist, parseName(v))
+ end
end
+ end
+ if #namelist == 0 then
+ error("missing the name of file/cmd/env!")
else
- return queryByFileName(name)
+ return namelist
end
end
-local function install(name)
- local list = query(name)
- installSomePackages(list)
+local function doQuery(arglist)
+ local namelist = parseArgList(arglist)
+ initPackageDB()
+ initDependencyDB()
+ query(namelist)
+end
+
+local function doInstall(arglist)
+ local namelist = parseArgList(arglist)
+ initPackageDB()
+ initDependencyDB()
+ install(namelist)
end
------------------------------------------------------------
---> \section{Respond to user input}
+--> \section{Print help or version text}
------------------------------------------------------------
local helptext = [[
usage: texfindpkg <action> [<options>] [<name>]
-Valid actions are:
+valid actions are:
install Install some package and its dependencies
query Query dependencies for some package
+ help Print this message and exit
+ version Print version information and exit
+
+valid options are:
+ -f Query or install by file name
+ -c Query or install by command name
+ -e Query or install by environment name
-Valid options are:
- --help Print this message and exit
- --version Print version information and exit
+please report bug at https://github.com/lvjr/texfindpkg
]]
-local function main()
- if arg[1] == nil then
- print(helptext)
- elseif arg[1] == "--help" then
- print(helptext)
- elseif arg[1] == "--version" then
- print("TeXFindPkg Version " .. tfpversion .. " (" .. tfpdate .. ")\n")
- elseif arg[1] == "install" then
- if arg[2] then
- initPackageDB()
- initDependencyDB()
- install(arg[2])
- else
- tfpPrint("missing the name of file/cmd/env!")
- end
- elseif arg[1] == "query" then
- if arg[2] then
- initPackageDB()
- initDependencyDB()
- local list = query(arg[2])
- listSomePackages(list)
- else
- tfpPrint("missing the name of file/cmd/env!")
- end
- elseif arg[1] == "generate" then
- generateJsonData()
- elseif arg[1] == "compare" then
- compareDistributions()
+local function help()
+ print(helptext)
+end
+
+local function version()
+ print("TeXFindPkg Version " .. tfp.version .. " (" .. tfp.date .. ")\n")
+end
+
+------------------------------------------------------------
+--> \section{Respond to user input}
+------------------------------------------------------------
+
+local function tfpMain(tfparg)
+ tfpresult = ""
+ if tfparg[1] == nil then return help() end
+ local action = remove(tfparg, 1)
+ action = match(action, "^%-*(.*)$") -- remove leading dashes
+ --print(action)
+ if action == "query" then
+ doQuery(tfparg)
+ elseif action == "install" then
+ doInstall(tfparg)
+ elseif action == "help" then
+ help()
+ elseif action == "version" then
+ version()
else
- tfpPrint("unknown option " .. arg[1])
+ tfpPrint("unknown action '" .. action .. "'")
+ help()
end
+ return tfpresult
end
-main()
+local function main()
+ tfpMain(arg)
+end
+
+if building then
+ tfp.tfpMain = tfpMain
+ tfp.showdbg = showdbg
+ tfp.dbgPrint = dbgPrint
+ tfp.tfpPrint = tfpPrint
+ tfp.fileRead = fileRead
+ tfp.fileWrite = fileWrite
+ tfp.getFiles = getFiles
+ tfp.valueExists = valueExists
+ tfp.json = json
+ tfp.gzip = gzip
+ tfp.tlParsePackageDB = tlParsePackageDB
+ tfp.mtParsePackageDB = mtParsePackageDB
+ return tfp
+else
+ main()
+end