diff options
Diffstat (limited to 'support/texfindpkg/texfindpkg.lua')
-rw-r--r-- | support/texfindpkg/texfindpkg.lua | 587 |
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 |