diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/data-res.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/data-res.lua | 1057 |
1 files changed, 503 insertions, 554 deletions
diff --git a/Master/texmf-dist/tex/context/base/data-res.lua b/Master/texmf-dist/tex/context/base/data-res.lua index d28a8a69767..73f3cc337e7 100644 --- a/Master/texmf-dist/tex/context/base/data-res.lua +++ b/Master/texmf-dist/tex/context/base/data-res.lua @@ -12,7 +12,7 @@ if not modules then modules = { } end modules ['data-res'] = { -- instance but for practical purposes we now avoid this and use a -- instance variable. We always have one instance active (sort of global). --- todo: cache:/// home:/// +-- todo: cache:/// home:/// selfautoparent:/// (sometime end 2012) local format, gsub, find, lower, upper, match, gmatch = string.format, string.gsub, string.find, string.lower, string.upper, string.match, string.gmatch local concat, insert, sortedkeys = table.concat, table.insert, table.sortedkeys @@ -55,22 +55,65 @@ resolvers.criticalvars = allocate { "SELFAUTOLOC", "SELFAUTODIR", "SELFAUTOPARE resolvers.luacnfname = 'texmfcnf.lua' resolvers.luacnfstate = "unknown" --- resolvers.luacnfspec = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}' -- what a rubish path - --- changed by Taco Hoekwater to make <root>/texmfnf.lua take precedence, --- while still allowing <root>/texmf/texmfnf.lua and --- <root>/texmf/web2c/texmfnf.lua (in that order) -resolvers.luacnfspec = '{selfautodir:,selfautoparent:};{selfautodir:,selfautoparent:}{/share,}/texmf{-local,};{selfautodir:,selfautoparent:}{/share,}/texmf{-local,}/web2c' - +-- The web2c tex binaries as well as kpse have built in paths for the configuration +-- files and there can be a depressing truckload of them. This is actually the weak +-- spot of a distribution. So we don't want: +-- +-- resolvers.luacnfspec = '{$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}' +-- +-- but instead use: +-- +-- resolvers.luacnfspec = 'selfautoparent:{/texmf{-local,}{,/web2c}}' +-- +-- which does not make texlive happy as there is a texmf-local tree one level up +-- (sigh), so we need this. (We can assume web2c as mkiv does not run on older +-- texlives anyway. +-- +-- texlive: +-- +-- selfautodir: +-- selfautoparent: +-- selfautodir:share/texmf-local/web2c +-- selfautodir:share/texmf/web2c +-- selfautodir:texmf-local/web2c +-- selfautodir:texmf/web2c +-- selfautoparent:share/texmf-local/web2c +-- selfautoparent:share/texmf/web2c +-- selfautoparent:texmf-local/web2c +-- selfautoparent:texmf/web2c +-- +-- minimals: +-- +-- home:texmf/web2c +-- selfautoparent:texmf-local/web2c +-- selfautoparent:texmf-context/web2c +-- selfautoparent:texmf/web2c + +if this_is_texlive then + -- resolvers.luacnfspec = '{selfautodir:,selfautoparent:}{,{/share,}/texmf{-local,}/web2c}' + -- resolvers.luacnfspec = '{selfautodir:{/share,}/texmf-local/web2c,selfautoparent:{/share,}/texmf{-local,}/web2c}' + -- resolvers.luacnfspec = 'selfautodir:/texmf-local/web2c;selfautoparent:/texmf{-local,}/web2c' + resolvers.luacnfspec = 'selfautodir:;selfautoparent:;{selfautodir:,selfautoparent:}{/share,}/texmf{-local,}/web2c' +else + resolvers.luacnfspec = 'home:texmf/web2c;selfautoparent:texmf{-local,-context,}/web2c' +end + +-- which (as we want users to use the web2c path) be can be simplified to this: +-- +-- if environment and environment.ownpath and string.find(environment.ownpath,"[\\/]texlive[\\/]") then +-- resolvers.luacnfspec = 'selfautodir:/texmf-local/web2c,selfautoparent:/texmf-local/web2c,selfautoparent:/texmf/web2c' +-- else +-- resolvers.luacnfspec = 'selfautoparent:/texmf-local/web2c,selfautoparent:/texmf/web2c' +-- end --~ -- not yet, some reporters expect strings --~ resolvers.luacnfspec = { ---~ "selfautoparent:/texmf-local", +--~ "selfautoparent:/texmf-local", -- is actually a user mistake --~ "selfautoparent:/texmf-local/web2c", ---~ "selfautoparent:/texmf", +--~ "selfautoparent:/texmf", -- idem --~ "selfautoparent:/texmf/web2c", ---~ "selfautoparent:", +--~ "selfautoparent:", -- idem --~ } local unset_variable = "unset" @@ -93,7 +136,7 @@ local instance = resolvers.instance or nil -- the current one (fast access) function resolvers.setenv(key,value,raw) if instance then -- this one will be consulted first when we stay inside - -- the current environment + -- the current environment (prefixes are not resolved here) instance.environment[key] = value -- we feed back into the environment, and as this is used -- by other applications (via os.execute) we need to make @@ -173,6 +216,7 @@ function resolvers.newinstance() -- todo: all vars will become lowercase and alp remember = true, diskcache = true, renewcache = false, + renewtree = false, loaderror = false, savelists = true, pattern = nil, -- lists @@ -264,7 +308,7 @@ local function makepathexpression(str) end end -local function reportcriticalvariables() +local function reportcriticalvariables(cnfspec) if trace_locating then for i=1,#resolvers.criticalvars do local k = resolvers.criticalvars[i] @@ -272,6 +316,14 @@ local function reportcriticalvariables() report_resolving("variable '%s' set to '%s'",k,v) end report_resolving() + if cnfspec then + if type(cnfspec) == "table" then + report_resolving("using configuration specification '%s'",concat(cnfspec,",")) + else + report_resolving("using configuration specification '%s'",cnfspec) + end + end + report_resolving() end reportcriticalvariables = function() end end @@ -286,7 +338,7 @@ local function identify_configuration_files() else resolvers.luacnfstate = "environment" end - reportcriticalvariables() + reportcriticalvariables(cnfspec) local cnfpaths = expandedpathfromlist(resolvers.splitpath(cnfspec)) local luacnfname = resolvers.luacnfname for i=1,#cnfpaths do @@ -322,6 +374,19 @@ local function load_configuration_files() if blob then local setups = instance.setups local data = blob() + local parent = data and data.parent + if parent then + local filename = filejoin(pathname,parent) + local realname = resolvers.resolve(filename) -- no shortcut + local blob = loadfile(realname) + if blob then + local parentdata = blob() + if parentdata then + report_resolving("loading configuration file '%s'",filename) + data = table.merged(parentdata,data) + end + end + end data = data and data.content if data then if trace_locating then @@ -411,11 +476,6 @@ local function locate_file_databases() local runtime = stripped == path path = resolvers.cleanpath(path) local spec = resolvers.splitmethod(stripped) - -- TH Original did this the other way around (elseif and if tests - -- reversed) but then the else branch was never reached, as 'runtime' - -- would effectively be ignored. In turn, that meant that the wrong - -- locator method was used, such that tree: and file: were treated - -- identically (cached and no runtime search). if runtime and (spec.noscheme or spec.scheme == "file") then stripped = "tree:///" .. stripped elseif spec.scheme == "cache" or spec.scheme == "file" then @@ -423,7 +483,7 @@ local function locate_file_databases() end if trace_locating then if runtime then - report_resolving("locating list of '%s' (runtime)",path) + report_resolving("locating list of '%s' (runtime) (%s)",path,stripped) else report_resolving("locating list of '%s' (cached)",path) end @@ -467,6 +527,39 @@ local function save_file_databases() -- will become cachers end end +function resolvers.renew(hashname) + if hashname and hashname ~= "" then + local expanded = resolvers.expansion(hashname) or "" + if expanded ~= "" then + if trace_locating then + report_resolving("identifying tree '%s' from '%s'",expanded,hashname) + end + hashname = expanded + else + if trace_locating then + report_resolving("identifying tree '%s'",hashname) + end + end + local realpath = resolvers.resolve(hashname) + if lfs.isdir(realpath) then + if trace_locating then + report_resolving("using path '%s'",realpath) + end + methodhandler('generators',hashname) + -- could be shared + local content = instance.files[hashname] + caches.collapsecontent(content) + if trace_locating then + report_resolving("saving tree '%s'",hashname) + end + caches.savecontent(hashname,"files",content) + -- till here + else + report_resolving("invalid path '%s'",realpath) + end + end +end + local function load_databases() locate_file_databases() if instance.diskcache and not instance.renewcache then @@ -754,6 +847,7 @@ local function collect_files(names) if dname == "" or find(dname,"^%.") then dname = false else +dname = gsub(dname,"*","%.*") dname = "/" .. dname .. "$" end local hashes = instance.hashes @@ -813,9 +907,22 @@ local function collect_files(names) return noffiles > 0 and filelist or nil end -function resolvers.registerintrees(name) - if not find(name,"^%.") then - instance.foundintrees[name] = (instance.foundintrees[name] or 0) + 1 -- maybe only one +local fit = { } + +function resolvers.registerintrees(filename,format,filetype,usedmethod,foundname) + local foundintrees = instance.foundintrees + if usedmethod == "direct" and filename == foundname and fit[foundname] then + -- just an extra lookup after a test on presence + else + local t = { + filename = filename, + format = format ~= "" and format or nil, + filetype = filetype ~= "" and filetype or nil, + usedmethod = usedmethod, + foundname = foundname, + } + fit[foundname] = t + foundintrees[#foundintrees+1] = t end end @@ -835,588 +942,423 @@ end local preparetreepattern = Cs((P(".")/"%%." + P("-")/"%%-" + P(1))^0 * Cc("$")) --- this one is split in smaller functions but it needs testing - -local function collect_instance_files(filename,askedformat,allresults) -- todo : plugin (scanners, checkers etc) - local result = { } - local stamp = nil - askedformat = askedformat or "" - filename = collapsepath(filename) - -- speed up / beware: format problem - if instance.remember and not allresults then - stamp = filename .. "--" .. askedformat - if instance.found[stamp] then +-- -- -- begin of main file search routing -- -- -- needs checking as previous has been patched + +local collect_instance_files + +local function find_analyze(filename,askedformat,allresults) + local filetype, wantedfiles, ext = '', { }, fileextname(filename) + -- too tricky as filename can be bla.1.2.3: + -- + -- if not suffixmap[ext] then + -- wantedfiles[#wantedfiles+1] = filename + -- end + wantedfiles[#wantedfiles+1] = filename + if askedformat == "" then + if ext == "" or not suffixmap[ext] then + local defaultsuffixes = resolvers.defaultsuffixes + for i=1,#defaultsuffixes do + local forcedname = filename .. '.' .. defaultsuffixes[i] + wantedfiles[#wantedfiles+1] = forcedname + filetype = resolvers.formatofsuffix(forcedname) + if trace_locating then + report_resolving("forcing filetype '%s'",filetype) + end + end + else + filetype = resolvers.formatofsuffix(filename) if trace_locating then - report_resolving("remembered file '%s'",filename) + report_resolving("using suffix based filetype '%s'",filetype) end - resolvers.registerintrees(filename) -- for tracing used files - return instance.found[stamp] end - end - if not dangerous[askedformat] then - if isreadable(filename) then - if trace_detail then - report_resolving("file '%s' found directly",filename) - end - if stamp then - instance.found[stamp] = { filename } + else + if ext == "" or not suffixmap[ext] then + local format_suffixes = suffixes[askedformat] + if format_suffixes then + for i=1,#format_suffixes do + wantedfiles[#wantedfiles+1] = filename .. "." .. format_suffixes[i] + end end - return { filename } + end + filetype = askedformat + if trace_locating then + report_resolving("using given filetype '%s'",filetype) end end + return filetype, wantedfiles +end + +local function find_direct(filename,allresults) + if not dangerous[askedformat] and isreadable(filename) then + if trace_detail then + report_resolving("file '%s' found directly",filename) + end + return "direct", { filename } + end +end + +local function find_wildcard(filename,allresults) if find(filename,'%*') then if trace_locating then report_resolving("checking wildcard '%s'", filename) end - result = resolvers.findwildcardfiles(filename) -- we can use th elocal - elseif file.is_qualified_path(filename) then - if isreadable(filename) then - if trace_locating then - report_resolving("qualified name '%s'", filename) - end - result = { filename } - else - local forcedname, ok, suffix = "", false, fileextname(filename) - if suffix == "" then -- why - local format_suffixes = askedformat == "" and resolvers.defaultsuffixes or suffixes[askedformat] - if format_suffixes then - for i=1,#format_suffixes do - local s = format_suffixes[i] - forcedname = filename .. "." .. s - if isreadable(forcedname) then - if trace_locating then - report_resolving("no suffix, forcing format filetype '%s'", s) - end - result, ok = { forcedname }, true - break - end - end - end - end - if not ok and suffix ~= "" then - -- try to find in tree (no suffix manipulation), here we search for the - -- matching last part of the name - local basename = filebasename(filename) - local pattern = lpegmatch(preparetreepattern,filename) - -- messy .. to be sorted out - local savedformat = askedformat - local format = savedformat or "" - if format == "" then - askedformat = resolvers.formatofsuffix(suffix) - end - if not format then - askedformat = "othertextfiles" -- kind of everything, maybe texinput is better - end - -- - if basename ~= filename then - local resolved = collect_instance_files(basename,askedformat,allresults) - if #result == 0 then -- shouldn't this be resolved ? - local lowered = lower(basename) - if filename ~= lowered then - resolved = collect_instance_files(lowered,askedformat,allresults) - end - end - resolvers.format = savedformat - -- - for r=1,#resolved do - local rr = resolved[r] - if find(rr,pattern) then - result[#result+1], ok = rr, true - end - end - end - -- a real wildcard: - -- - -- if not ok then - -- local filelist = collect_files({basename}) - -- for f=1,#filelist do - -- local ff = filelist[f][3] or "" - -- if find(ff,pattern) then - -- result[#result+1], ok = ff, true - -- end - -- end - -- end - end - if not ok and trace_locating then - report_resolving("qualified name '%s'", filename) - end + local method, result = resolvers.findwildcardfiles(filename) + if result then + return "wildcard", result end - else - -- search spec - local filetype, done, wantedfiles, ext = '', false, { }, fileextname(filename) - -- -- tricky as filename can be bla.1.2.3 - -- if not suffixmap[ext] then --- probably needs to be done elsewhere too - -- wantedfiles[#wantedfiles+1] = filename - -- end - wantedfiles[#wantedfiles+1] = filename - if askedformat == "" then - if ext == "" or not suffixmap[ext] then - local defaultsuffixes = resolvers.defaultsuffixes - for i=1,#defaultsuffixes do - local forcedname = filename .. '.' .. defaultsuffixes[i] - wantedfiles[#wantedfiles+1] = forcedname - filetype = resolvers.formatofsuffix(forcedname) + end +end + +local function find_qualified(filename,allresults) -- this one will be split too + if not file.is_qualified_path(filename) then + return + end + if trace_locating then + report_resolving("checking qualified name '%s'", filename) + end + if isreadable(filename) then + if trace_detail then + report_resolving("qualified file '%s' found", filename) + end + return "qualified", { filename } + end + if trace_detail then + report_resolving("locating qualified file '%s'", filename) + end + local forcedname, suffix = "", fileextname(filename) + if suffix == "" then -- why + local format_suffixes = askedformat == "" and resolvers.defaultsuffixes or suffixes[askedformat] + if format_suffixes then + for i=1,#format_suffixes do + local s = format_suffixes[i] + forcedname = filename .. "." .. s + if isreadable(forcedname) then if trace_locating then - report_resolving("forcing filetype '%s'",filetype) + report_resolving("no suffix, forcing format filetype '%s'", s) end + return "qualified", { forcedname } end - else - filetype = resolvers.formatofsuffix(filename) - if trace_locating then - report_resolving("using suffix based filetype '%s'",filetype) + end + end + end + if suffix and suffix ~= "" then + -- try to find in tree (no suffix manipulation), here we search for the + -- matching last part of the name + local basename = filebasename(filename) + local pattern = lpegmatch(preparetreepattern,filename) + -- messy .. to be sorted out + local savedformat = askedformat + local format = savedformat or "" + if format == "" then + askedformat = resolvers.formatofsuffix(suffix) + end + if not format then + askedformat = "othertextfiles" -- kind of everything, maybe all + end + -- + if basename ~= filename then + local resolved = collect_instance_files(basename,askedformat,allresults) + if #resolved == 0 then + local lowered = lower(basename) + if filename ~= lowered then + resolved = collect_instance_files(lowered,askedformat,allresults) end end - else - if ext == "" or not suffixmap[ext] then - local format_suffixes = suffixes[askedformat] - if format_suffixes then - for i=1,#format_suffixes do - wantedfiles[#wantedfiles+1] = filename .. "." .. format_suffixes[i] + resolvers.format = savedformat + -- + if #resolved > 0 then + local result = { } + for r=1,#resolved do + local rr = resolved[r] + if find(rr,pattern) then + result[#result+1] = rr end end - end - filetype = askedformat - if trace_locating then - report_resolving("using given filetype '%s'",filetype) + if #result > 0 then + return "qualified", result + end end end - local typespec = resolvers.variableofformat(filetype) - local pathlist = resolvers.expandedpathlist(typespec) - if not pathlist or #pathlist == 0 then - -- no pathlist, access check only / todo == wildcard - if trace_detail then - report_resolving("checking filename '%s', filetype '%s', wanted files '%s'",filename, filetype or '?',concat(wantedfiles," | ")) - end - for k=1,#wantedfiles do - local fname = wantedfiles[k] - if fname and isreadable(fname) then - filename, done = fname, true - result[#result+1] = filejoin('.',fname) - break - end + -- a real wildcard: + -- + -- local filelist = collect_files({basename}) + -- result = { } + -- for f=1,#filelist do + -- local ff = filelist[f][3] or "" + -- if find(ff,pattern) then + -- result[#result+1], ok = ff, true + -- end + -- end + -- if #result > 0 then + -- return "qualified", result + -- end + end +end + +local function check_subpath(fname) + if isreadable(fname) then + if trace_detail then + report_resolving("found '%s' by deep scanning",fname) + end + return fname + end +end + +local function find_intree(filename,filetype,wantedfiles,allresults) + local typespec = resolvers.variableofformat(filetype) + local pathlist = resolvers.expandedpathlist(typespec) + local method = "intree" + if pathlist and #pathlist > 0 then + -- list search + local filelist = collect_files(wantedfiles) + local dirlist = { } + if filelist then + for i=1,#filelist do + dirlist[i] = filedirname(filelist[i][3]) .. "/" -- was [2] .. gamble end - -- this is actually 'other text files' or 'any' or 'whatever' - local filelist = collect_files(wantedfiles) - local fl = filelist and filelist[1] - if fl then - filename = fl[3] -- not local? - result[#result+1] = resolvers.resolve(filename) - done = true + end + if trace_detail then + report_resolving("checking filename '%s'",filename) + end + local result = { } + for k=1,#pathlist do + local path = pathlist[k] + local pathname = lpegmatch(inhibitstripper,path) + local doscan = path == pathname -- no ^!! + if not find (pathname,'//$') then + doscan = false -- we check directly on the path end - else - -- list search - local filelist = collect_files(wantedfiles) - local dirlist = { } - if filelist then - for i=1,#filelist do - dirlist[i] = filedirname(filelist[i][3]) .. "/" -- was [2] .. gamble + local done = false + -- using file list + if filelist then -- database + -- compare list entries with permitted pattern -- /xx /xx// + local expression = makepathexpression(pathname) + if trace_detail then + report_resolving("using pattern '%s' for path '%s'",expression,pathname) end - end - if trace_detail then - report_resolving("checking filename '%s'",filename) - end - for k=1,#pathlist do - local path = pathlist[k] - local pathname = lpegmatch(inhibitstripper,path) - local doscan = path == pathname -- no ^!! - if not find (pathname, '//$') then doscan = false end - done = false - -- using file list - if filelist then - -- compare list entries with permitted pattern -- /xx /xx// - local expression = makepathexpression(pathname) - if trace_detail then - report_resolving("using pattern '%s' for path '%s'",expression,pathname) - end - for k=1,#filelist do - local fl = filelist[k] - local f = fl[2] - local d = dirlist[k] - if find(d,expression) then - -- todo, test for readable - result[#result+1] = resolvers.resolve(fl[3]) -- no shortcut - done = true - if allresults then - if trace_detail then - report_resolving("match to '%s' in hash for file '%s' and path '%s', continue scanning",expression,f,d) - end - else - if trace_detail then - report_resolving("match to '%s' in hash for file '%s' and path '%s', quit scanning",expression,f,d) - end - break + for k=1,#filelist do + local fl = filelist[k] + local f = fl[2] + local d = dirlist[k] + if find(d,expression) then + -- todo, test for readable + result[#result+1] = resolvers.resolve(fl[3]) -- no shortcut + done = true + if allresults then + if trace_detail then + report_resolving("match to '%s' in hash for file '%s' and path '%s', continue scanning",expression,f,d) end - elseif trace_detail then - report_resolving("no match to '%s' in hash for file '%s' and path '%s'",expression,f,d) + else + if trace_detail then + report_resolving("match to '%s' in hash for file '%s' and path '%s', quit scanning",expression,f,d) + end + break end + elseif trace_detail then + report_resolving("no match to '%s' in hash for file '%s' and path '%s'",expression,f,d) end end - if not done then - -- check if on disk / unchecked / does not work at all / also zips - -- TH perhaps it did not work because of missing resolvers.resolve() - -- and resolvers.scanfiles() calls ... - pathname = resolvers.resolve(pathname) - local scheme = url.hasscheme(pathname) - if not scheme or scheme == "file" then - local pname = gsub(pathname,"%.%*$",'') - if not find(pname,"%*") then - local ppname = gsub(pname,"/+$","") - if can_be_dir(ppname) then - local files = {} - if doscan then files = resolvers.scanfiles(ppname) end + end + if done then + method = "database" + else + method = "filesystem" -- bonus, even when !! is specified + pathname = gsub(pathname,"/+$","") + pathname = resolvers.resolve(pathname) + local scheme = url.hasscheme(pathname) + if not scheme or scheme == "file" then + local pname = gsub(pathname,"%.%*$",'') + if not find(pname,"%*") then + if can_be_dir(pname) then + -- quick root scan first + for k=1,#wantedfiles do + local w = wantedfiles[k] + local fname = check_subpath(filejoin(pname,w)) + if fname then + result[#result+1] = fname + done = true + if not allresults then + break + end + end + end + if not done and doscan then + -- collect files in path (and cache the result) + local files = resolvers.simplescanfiles(pname,false,true) for k=1,#wantedfiles do local w = wantedfiles[k] - local subpath = '' - if files[w] then - subpath = type(files[w]) == "table" and files[w][1] or files[w] - end - local fname = filejoin(ppname,subpath,w) - if isreadable(fname) then - if trace_detail then - report_resolving("found '%s' by scanning",fname) + local subpath = files[w] + if not subpath or subpath == "" then + -- rootscan already done + elseif type(subpath) == "string" then + local fname = check_subpath(filejoin(pname,subpath,w)) + if fname then + result[#result+1] = fname + done = true + if not allresults then + break + end + end + else + for i=1,#subpath do + local sp = subpath[i] + if sp == "" then + -- roottest already done + else + local fname = check_subpath(filejoin(pname,sp,w)) + if fname then + result[#result+1] = fname + done = true + if not allresults then + break + end + end + end + end + if done and not allresults then + break end - result[#result+1] = fname - done = true - if not allresults then break end end end - else - -- no access needed for non existing path, speedup (esp in large tree with lots of fake) end end + else + -- no access needed for non existing path, speedup (esp in large tree with lots of fake) end end - if not done and doscan then - -- todo: slow path scanning ... although we now have tree:// supported in $TEXMF - end - if done and not allresults then break end + end + -- todo recursive scanning + if done and not allresults then + break end end + if #result > 0 then + return method, result + end end - for k=1,#result do - local rk = collapsepath(result[k]) - result[k] = rk - resolvers.registerintrees(rk) -- for tracing used files +end + +local function find_onpath(filename,filetype,wantedfiles,allresults) + if trace_detail then + report_resolving("checking filename '%s', filetype '%s', wanted files '%s'",filename, filetype or '?',concat(wantedfiles," | ")) end - if stamp then - instance.found[stamp] = result + local result = { } + for k=1,#wantedfiles do + local fname = wantedfiles[k] + if fname and isreadable(fname) then + filename = fname + result[#result+1] = filejoin('.',fname) + if not allresults then + break + end + end + end + if #result > 0 then + return "onpath", result + end +end + +local function find_otherwise(filename,filetype,wantedfiles,allresults) -- other text files | any | whatever + local filelist = collect_files(wantedfiles) + local fl = filelist and filelist[1] + if fl then + return "otherwise", { resolvers.resolve(fl[3]) } -- filename end - return result end --- -- -- begin of main file search routing -- -- -- - ---~ local collect_instance_files - ---~ local function find_direct(filename) ---~ if not dangerous[askedformat] and isreadable(filename) then ---~ if trace_detail then ---~ report_resolving("file '%s' found directly",filename) ---~ end ---~ return { filename } ---~ end ---~ end - ---~ local function find_wildcard(filename) ---~ if find(filename,'%*') then ---~ if trace_locating then ---~ report_resolving("checking wildcard '%s'", filename) ---~ end ---~ return resolvers.findwildcardfiles(filename) -- we can use the local ---~ end ---~ end - ---~ local function find_qualified(filename) -- this one will be split too ---~ if not file.is_qualified_path(filename) then ---~ return ---~ end ---~ if trace_locating then ---~ report_resolving("checking qualified name '%s'", filename) ---~ end ---~ if isreadable(filename) then ---~ if trace_detail then ---~ report_resolving("qualified file '%s' found", filename) ---~ end ---~ return { filename } ---~ else ---~ if trace_detail then ---~ report_resolving("locating qualified file '%s'", filename) ---~ end ---~ local forcedname, suffix = "", fileextname(filename) ---~ if suffix == "" then -- why ---~ local format_suffixes = askedformat == "" and resolvers.defaultsuffixes or suffixes[askedformat] ---~ if format_suffixes then ---~ for i=1,#format_suffixes do ---~ local s = format_suffixes[i] ---~ forcedname = filename .. "." .. s ---~ if isreadable(forcedname) then ---~ if trace_locating then ---~ report_resolving("no suffix, forcing format filetype '%s'", s) ---~ end ---~ return { forcedname } ---~ end ---~ end ---~ end ---~ end ---~ if suffix ~= "" then ---~ -- try to find in tree (no suffix manipulation), here we search for the ---~ -- matching last part of the name ---~ local basename = filebasename(filename) ---~ local pattern = lpegmatch(preparetreepattern,filename) ---~ -- messy .. to be sorted out ---~ local savedformat = askedformat ---~ local format = savedformat or "" ---~ if format == "" then ---~ askedformat = resolvers.formatofsuffix(suffix) ---~ end ---~ if not format then ---~ askedformat = "othertextfiles" -- kind of everything, maybe all ---~ end ---~ -- ---~ if basename ~= filename then ---~ local resolved = collect_instance_files(basename,askedformat,allresults) ---~ if #resolved == 0 then ---~ local lowered = lower(basename) ---~ if filename ~= lowered then ---~ resolved = collect_instance_files(lowered,askedformat,allresults) ---~ end ---~ end ---~ resolvers.format = savedformat ---~ -- ---~ if #resolved > 0 then ---~ local result = { } ---~ for r=1,#resolved do ---~ local rr = resolved[r] ---~ if find(rr,pattern) then ---~ result[#result+1] = rr ---~ end ---~ end ---~ if #result > 0 then ---~ return result ---~ end ---~ end ---~ end ---~ -- a real wildcard: ---~ -- ---~ -- local filelist = collect_files({basename}) ---~ -- result = { } ---~ -- for f=1,#filelist do ---~ -- local ff = filelist[f][3] or "" ---~ -- if find(ff,pattern) then ---~ -- result[#result+1], ok = ff, true ---~ -- end ---~ -- end ---~ -- if #result > 0 then ---~ -- return result ---~ -- end ---~ end ---~ end ---~ end - ---~ local function find_analyze(filename,askedformat) ---~ local filetype, wantedfiles, ext = '', { }, fileextname(filename) ---~ -- too tricky as filename can be bla.1.2.3: ---~ -- ---~ -- if not suffixmap[ext] then ---~ -- wantedfiles[#wantedfiles+1] = filename ---~ -- end ---~ wantedfiles[#wantedfiles+1] = filename ---~ if askedformat == "" then ---~ if ext == "" or not suffixmap[ext] then ---~ local defaultsuffixes = resolvers.defaultsuffixes ---~ for i=1,#defaultsuffixes do ---~ local forcedname = filename .. '.' .. defaultsuffixes[i] ---~ wantedfiles[#wantedfiles+1] = forcedname ---~ filetype = resolvers.formatofsuffix(forcedname) ---~ if trace_locating then ---~ report_resolving("forcing filetype '%s'",filetype) ---~ end ---~ end ---~ else ---~ filetype = resolvers.formatofsuffix(filename) ---~ if trace_locating then ---~ report_resolving("using suffix based filetype '%s'",filetype) ---~ end ---~ end ---~ else ---~ if ext == "" or not suffixmap[ext] then ---~ local format_suffixes = suffixes[askedformat] ---~ if format_suffixes then ---~ for i=1,#format_suffixes do ---~ wantedfiles[#wantedfiles+1] = filename .. "." .. format_suffixes[i] ---~ end ---~ end ---~ end ---~ filetype = askedformat ---~ if trace_locating then ---~ report_resolving("using given filetype '%s'",filetype) ---~ end ---~ end ---~ return filetype, wantedfiles ---~ end - ---~ local function find_intree(filename,filetype,wantedfiles) ---~ local typespec = resolvers.variableofformat(filetype) ---~ local pathlist = resolvers.expandedpathlist(typespec) ---~ if pathlist and #pathlist > 0 then ---~ -- list search ---~ local filelist = collect_files(wantedfiles) ---~ local dirlist = { } ---~ if filelist then ---~ for i=1,#filelist do ---~ dirlist[i] = filedirname(filelist[i][3]) .. "/" -- was [2] .. gamble ---~ end ---~ end ---~ if trace_detail then ---~ report_resolving("checking filename '%s'",filename) ---~ end ---~ local result = { } ---~ for k=1,#pathlist do ---~ local path = pathlist[k] ---~ local pathname = lpegmatch(inhibitstripper,path) ---~ local doscan = path == pathname -- no ^!! ---~ local done = false ---~ -- using file list ---~ if filelist then ---~ -- compare list entries with permitted pattern -- /xx /xx// ---~ local expression = makepathexpression(pathname) ---~ if trace_detail then ---~ report_resolving("using pattern '%s' for path '%s'",expression,pathname) ---~ end ---~ for k=1,#filelist do ---~ local fl = filelist[k] ---~ local f = fl[2] ---~ local d = dirlist[k] ---~ if find(d,expression) then ---~ -- todo, test for readable ---~ result[#result+1] = resolvers.resolve(fl[3]) -- no shortcut ---~ done = true ---~ if allresults then ---~ if trace_detail then ---~ report_resolving("match to '%s' in hash for file '%s' and path '%s', continue scanning",expression,f,d) ---~ end ---~ else ---~ if trace_detail then ---~ report_resolving("match to '%s' in hash for file '%s' and path '%s', quit scanning",expression,f,d) ---~ end ---~ break ---~ end ---~ elseif trace_detail then ---~ report_resolving("no match to '%s' in hash for file '%s' and path '%s'",expression,f,d) ---~ end ---~ end ---~ end ---~ if not done and doscan then ---~ -- check if on disk / unchecked / does not work at all / also zips ---~ local scheme = url.hasscheme(pathname) ---~ if not scheme or scheme == "file" then ---~ local pname = gsub(pathname,"%.%*$",'') ---~ if not find(pname,"%*") then ---~ local ppname = gsub(pname,"/+$","") ---~ if can_be_dir(ppname) then ---~ for k=1,#wantedfiles do ---~ local w = wantedfiles[k] ---~ local fname = filejoin(ppname,w) ---~ if isreadable(fname) then ---~ if trace_detail then ---~ report_resolving("found '%s' by scanning",fname) ---~ end ---~ result[#result+1] = fname ---~ done = true ---~ if not allresults then break end ---~ end ---~ end ---~ else ---~ -- no access needed for non existing path, speedup (esp in large tree with lots of fake) ---~ end ---~ end ---~ end ---~ end ---~ if not done and doscan then ---~ -- todo: slow path scanning ... although we now have tree:// supported in $TEXMF ---~ end ---~ if done and not allresults then ---~ return #result > 0 and result ---~ end ---~ end ---~ end ---~ end - ---~ local function find_onpath(filename,filetype,wantedfiles) ---~ local done = nil ---~ if trace_detail then ---~ report_resolving("checking filename '%s', filetype '%s', wanted files '%s'",filename, filetype or '?',concat(wantedfiles," | ")) ---~ end ---~ for k=1,#wantedfiles do ---~ local fname = wantedfiles[k] ---~ if fname and isreadable(fname) then ---~ filename, done = fname, true ---~ result[#result+1] = filejoin('.',fname) ---~ break ---~ end ---~ end ---~ end - ---~ local function find_otherwise(filename,filetype,wantedfiles) -- other text files | any | whatever ---~ local filelist = collect_files(wantedfiles) ---~ local fl = filelist and filelist[1] ---~ if fl then ---~ return { resolvers.resolve(fl[3]) } -- filename ---~ end ---~ end - ---~ collect_instance_files = function(filename,askedformat,allresults) -- uses nested ---~ local result, stamp, filetype, wantedfiles ---~ askedformat = askedformat or "" ---~ filename = collapsepath(filename) ---~ if instance.remember and not allresults then ---~ stamp = format("%s--%s", filename, askedformat) ---~ result = stamp and instance.found[stamp] ---~ if result then ---~ if trace_locating then ---~ report_resolving("remembered file '%s'",filename) ---~ end ---~ return result ---~ end ---~ end ---~ result = find_direct (filename,stamp) or ---~ find_wildcard (filename) or ---~ find_qualified(filename) ---~ if not result then ---~ filetype, wantedfiles = find_analyze(filename,askedformat) ---~ result = find_intree (filename,filetype,wantedfiles) or ---~ find_onpath (filename,filetype,wantedfiles) or ---~ find_otherwise(filename,filetype,wantedfiles) ---~ end ---~ if result then ---~ for k=1,#result do ---~ local rk = collapsepath(result[k]) ---~ result[k] = rk ---~ resolvers.registerintrees(rk) -- for tracing used files ---~ end ---~ else ---~ result = { } -- maybe false ---~ end ---~ if stamp then ---~ if trace_locating then ---~ report_resolving("remembering file '%s'",filename) ---~ end ---~ instance.found[stamp] = result ---~ end ---~ return result ---~ end +-- we could have a loop over the 6 functions but then we'd have to +-- always analyze + +collect_instance_files = function(filename,askedformat,allresults) -- uses nested + askedformat = askedformat or "" + filename = collapsepath(filename) + if allresults then + -- no need for caching, only used for tracing + local filetype, wantedfiles = find_analyze(filename,askedformat) + local results = { + { find_direct (filename,true) }, + { find_wildcard (filename,true) }, + { find_qualified(filename,true) }, + { find_intree (filename,filetype,wantedfiles,true) }, + { find_onpath (filename,filetype,wantedfiles,true) }, + { find_otherwise(filename,filetype,wantedfiles,true) }, + } + local result, status, done = { }, { }, { } + for k, r in next, results do + local method, list = r[1], r[2] + if method and list then + for i=1,#list do + local c = collapsepath(list[i]) + if not done[c] then + result[#result+1] = c + done[c] = true + end + status[#status+1] = format("%-10s: %s",method,c) + end + end + end + if trace_detail then + report_resolving("lookup status: %s",table.serialize(status,filename)) + end + return result, status + else + local method, result, stamp, filetype, wantedfiles + if instance.remember then + stamp = format("%s--%s", filename, askedformat) + result = stamp and instance.found[stamp] + if result then + if trace_locating then + report_resolving("remembered file '%s'",filename) + end + return result + end + end + method, result = find_direct(filename) + if not result then + method, result = find_wildcard(filename) + if not result then + method, result = find_qualified(filename) + if not result then + filetype, wantedfiles = find_analyze(filename,askedformat) + method, result = find_intree(filename,filetype,wantedfiles) + if not result then + method, result = find_onpath(filename,filetype,wantedfiles) + if not result then + method, result = find_otherwise(filename,filetype,wantedfiles) + end + end + end + end + end + if result and #result > 0 then + local foundname = collapsepath(result[1]) + resolvers.registerintrees(filename,askedformat,filetype,method,foundname) + result = { foundname } + else + result = { } -- maybe false + end + if stamp then + if trace_locating then + report_resolving("remembering file '%s'",filename) + end + instance.found[stamp] = result + end + return result + end +end -- -- -- end of main file search routing -- -- -- + local function findfiles(filename,filetype,allresults) - local result = collect_instance_files(filename,filetype or "",allresults) - if #result == 0 then + local result, status = collect_instance_files(filename,filetype or "",allresults) + if not result or #result == 0 then local lowered = lower(filename) if filename ~= lowered then - return collect_instance_files(lowered,filetype or "",allresults) + result, status = collect_instance_files(lowered,filetype or "",allresults) end end - return result + return result or { }, status end function resolvers.findfiles(filename,filetype) @@ -1583,6 +1525,10 @@ function resolvers.load(option) return files and next(files) and true end +function resolvers.loadtime() + return statistics.elapsedtime(instance) +end + local function report(str) if trace_locating then report_resolving(str) -- has already verbose @@ -1596,6 +1542,9 @@ function resolvers.dowithfilesandreport(command, files, ...) -- will move if trace_locating then report('') -- ? end + if type(files) == "string" then + files = { files } + end for f=1,#files do local file = files[f] local result = command(file,...) |