diff options
Diffstat (limited to 'tex/context/base/trac-log.lua')
-rw-r--r-- | tex/context/base/trac-log.lua | 1632 |
1 files changed, 816 insertions, 816 deletions
diff --git a/tex/context/base/trac-log.lua b/tex/context/base/trac-log.lua index 73e302e26..1f2520130 100644 --- a/tex/context/base/trac-log.lua +++ b/tex/context/base/trac-log.lua @@ -1,816 +1,816 @@ -if not modules then modules = { } end modules ['trac-log'] = {
- version = 1.001,
- comment = "companion to trac-log.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- if tex and (tex.jobname or tex.formatname) then
---
--- -- quick hack, awaiting speedup in engine (8 -> 6.4 sec for --make with console2)
--- -- still needed for luajittex
---
--- local texio_write_nl = texio.write_nl
--- local texio_write = texio.write
--- local io_write = io.write
-
--- local write_nl = function(target,...)
--- if not io_write then
--- io_write = io.write
--- end
--- if target == "term and log" then
--- texio_write_nl("log",...)
--- texio_write_nl("term","")
--- io_write(...)
--- elseif target == "log" then
--- texio_write_nl("log",...)
--- elseif target == "term" then
--- texio_write_nl("term","")
--- io_write(...)
--- else
--- texio_write_nl("log",target,...)
--- texio_write_nl("term","")
--- io_write(target,...)
--- end
--- end
-
--- local write = function(target,...)
--- if not io_write then
--- io_write = io.write
--- end
--- if target == "term and log" then
--- texio_write("log",...)
--- io_write(...)
--- elseif target == "log" then
--- texio_write("log",...)
--- elseif target == "term" then
--- io_write(...)
--- else
--- texio_write("log",target,...)
--- io_write(target,...)
--- end
--- end
-
--- texio.write = write
--- texio.write_nl = write_nl
---
--- else
---
--- -- texlua or just lua
---
--- end
-
--- todo: less categories, more subcategories (e.g. nodes)
--- todo: split into basics and ctx specific
-
-local write_nl, write = texio and texio.write_nl or print, texio and texio.write or io.write
-local format, gmatch, find = string.format, string.gmatch, string.find
-local concat, insert, remove = table.concat, table.insert, table.remove
-local topattern = string.topattern
-local texcount = tex and tex.count
-local next, type, select = next, type, select
-local utfchar = utf.char
-
-local setmetatableindex = table.setmetatableindex
-local formatters = string.formatters
-
---[[ldx--
-<p>This is a prelude to a more extensive logging module. We no longer
-provide <l n='xml'/> based logging as parsing is relatively easy anyway.</p>
---ldx]]--
-
-logs = logs or { }
-local logs = logs
-
-local moreinfo = [[
-More information about ConTeXt and the tools that come with it can be found at:
-]] .. "\n" .. [[
-maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
-webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
-wiki : http://contextgarden.net
-]]
-
--- -- we extend the formatters:
---
--- function utilities.strings.unichr(s) return "U+" .. format("%05X",s) .. " (" .. utfchar(s) .. ")" end
--- function utilities.strings.chruni(s) return utfchar(s) .. " (U+" .. format("%05X",s) .. ")" end
---
--- utilities.strings.formatters.add (
--- string.formatters, "uni",
--- [[unichr(%s)]],
--- [[local unichr = utilities.strings.unichr]]
--- )
---
--- utilities.strings.formatters.add (
--- string.formatters, "chr",
--- [[chruni(%s)]],
--- [[local chruni = utilities.strings.chruni]]
--- )
-
-utilities.strings.formatters.add (
- formatters, "unichr",
- [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]]
-)
-
-utilities.strings.formatters.add (
- formatters, "chruni",
- [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]]
-)
-
--- print(formatters["Missing character %!chruni! in font."](234))
--- print(formatters["Missing character %!unichr! in font."](234))
-
--- basic loggers
-
-local function ignore() end
-
-setmetatableindex(logs, function(t,k) t[k] = ignore ; return ignore end)
-
-local report, subreport, status, settarget, setformats, settranslations
-
-local direct, subdirect, writer, pushtarget, poptarget
-
-if tex and (tex.jobname or tex.formatname) then
-
- -- local format = string.formatter
-
- local valueiskey = { __index = function(t,k) t[k] = k return k end } -- will be helper
-
- local target = "term and log"
-
- logs.flush = io.flush
-
- local formats = { } setmetatable(formats, valueiskey)
- local translations = { } setmetatable(translations,valueiskey)
-
- writer = function(...)
- write_nl(target,...)
- end
-
- newline = function()
- write_nl(target,"\n")
- end
-
- local f_one = formatters["%-15s > %s\n"]
- local f_two = formatters["%-15s >\n"]
-
- -- we can use formatters but best check for % then because for simple messages
- -- we con't want this overhead for single messages (not that there are that
- -- many; we could have a special weak table)
-
- report = function(a,b,c,...)
- if c then
- write_nl(target,f_one(translations[a],formatters[formats[b]](c,...)))
- elseif b then
- write_nl(target,f_one(translations[a],formats[b]))
- elseif a then
- write_nl(target,f_two(translations[a]))
- else
- write_nl(target,"\n")
- end
- end
-
- local f_one = formatters["%-15s > %s"]
- local f_two = formatters["%-15s >"]
-
- direct = function(a,b,c,...)
- if c then
- return f_one(translations[a],formatters[formats[b]](c,...))
- elseif b then
- return f_one(translations[a],formats[b])
- elseif a then
- return f_two(translations[a])
- else
- return ""
- end
- end
-
- local f_one = formatters["%-15s > %s > %s\n"]
- local f_two = formatters["%-15s > %s >\n"]
-
- subreport = function(a,s,b,c,...)
- if c then
- write_nl(target,f_one(translations[a],translations[s],formatters[formats[b]](c,...)))
- elseif b then
- write_nl(target,f_one(translations[a],translations[s],formats[b]))
- elseif a then
- write_nl(target,f_two(translations[a],translations[s]))
- else
- write_nl(target,"\n")
- end
- end
-
- local f_one = formatters["%-15s > %s > %s"]
- local f_two = formatters["%-15s > %s >"]
-
- subdirect = function(a,s,b,c,...)
- if c then
- return f_one(translations[a],translations[s],formatters[formats[b]](c,...))
- elseif b then
- return f_one(translations[a],translations[s],formats[b])
- elseif a then
- return f_two(translations[a],translations[s])
- else
- return ""
- end
- end
-
- local f_one = formatters["%-15s : %s\n"]
- local f_two = formatters["%-15s :\n"]
-
- status = function(a,b,c,...)
- if c then
- write_nl(target,f_one(translations[a],formatters[formats[b]](c,...)))
- elseif b then
- write_nl(target,f_one(translations[a],formats[b]))
- elseif a then
- write_nl(target,f_two(translations[a]))
- else
- write_nl(target,"\n")
- end
- end
-
- local targets = {
- logfile = "log",
- log = "log",
- file = "log",
- console = "term",
- terminal = "term",
- both = "term and log",
- }
-
- settarget = function(whereto)
- target = targets[whereto or "both"] or targets.both
- if target == "term" or target == "term and log" then
- logs.flush = io.flush
- else
- logs.flush = ignore
- end
- end
-
- local stack = { }
-
- pushtarget = function(newtarget)
- insert(stack,target)
- settarget(newtarget)
- end
-
- poptarget = function()
- if #stack > 0 then
- settarget(remove(stack))
- end
- end
-
- setformats = function(f)
- formats = f
- end
-
- settranslations = function(t)
- translations = t
- end
-
-else
-
- logs.flush = ignore
-
- writer = write_nl
-
- newline = function()
- write_nl("\n")
- end
-
- local f_one = formatters["%-15s | %s"]
- local f_two = formatters["%-15s |"]
-
- report = function(a,b,c,...)
- if c then
- write_nl(f_one(a,formatters[b](c,...)))
- elseif b then
- write_nl(f_one(a,b))
- elseif a then
- write_nl(f_two(a))
- else
- write_nl("")
- end
- end
-
- local f_one = formatters["%-15s | %s | %s"]
- local f_two = formatters["%-15s | %s |"]
-
- subreport = function(a,sub,b,c,...)
- if c then
- write_nl(f_one(a,sub,formatters[b](c,...)))
- elseif b then
- write_nl(f_one(a,sub,b))
- elseif a then
- write_nl(f_two(a,sub))
- else
- write_nl("")
- end
- end
-
- local f_one = formatters["%-15s : %s\n"]
- local f_two = formatters["%-15s :\n"]
-
- status = function(a,b,c,...) -- not to be used in lua anyway
- if c then
- write_nl(f_one(a,formatters[b](c,...)))
- elseif b then
- write_nl(f_one(a,b)) -- b can have %'s
- elseif a then
- write_nl(f_two(a))
- else
- write_nl("\n")
- end
- end
-
- direct = ignore
- subdirect = ignore
-
- settarget = ignore
- pushtarget = ignore
- poptarget = ignore
- setformats = ignore
- settranslations = ignore
-
-end
-
-logs.report = report
-logs.subreport = subreport
-logs.status = status
-logs.settarget = settarget
-logs.pushtarget = pushtarget
-logs.poptarget = poptarget
-logs.setformats = setformats
-logs.settranslations = settranslations
-
-logs.direct = direct
-logs.subdirect = subdirect
-logs.writer = writer
-logs.newline = newline
-
--- installer
-
--- todo: renew (un) locks when a new one is added and wildcard
-
-local data, states = { }, nil
-
-function logs.reporter(category,subcategory)
- local logger = data[category]
- if not logger then
- local state = false
- if states == true then
- state = true
- elseif type(states) == "table" then
- for c, _ in next, states do
- if find(category,c) then
- state = true
- break
- end
- end
- end
- logger = {
- reporters = { },
- state = state,
- }
- data[category] = logger
- end
- local reporter = logger.reporters[subcategory or "default"]
- if not reporter then
- if subcategory then
- reporter = function(...)
- if not logger.state then
- subreport(category,subcategory,...)
- end
- end
- logger.reporters[subcategory] = reporter
- else
- local tag = category
- reporter = function(...)
- if not logger.state then
- report(category,...)
- end
- end
- logger.reporters.default = reporter
- end
- end
- return reporter
-end
-
-logs.new = logs.reporter -- for old times sake
-
--- context specicific: this ends up in the macro stream
-
-local ctxreport = logs.writer
-
-function logs.setmessenger(m)
- ctxreport = m
-end
-
-function logs.messenger(category,subcategory)
- -- we need to avoid catcode mess (todo: fast context)
- if subcategory then
- return function(...)
- ctxreport(subdirect(category,subcategory,...))
- end
- else
- return function(...)
- ctxreport(direct(category,...))
- end
- end
-end
-
--- so far
-
-local function setblocked(category,value)
- if category == true then
- -- lock all
- category, value = "*", true
- elseif category == false then
- -- unlock all
- category, value = "*", false
- elseif value == nil then
- -- lock selective
- value = true
- end
- if category == "*" then
- states = value
- for k, v in next, data do
- v.state = value
- end
- else
- states = utilities.parsers.settings_to_hash(category)
- for c, _ in next, states do
- if data[c] then
- v.state = value
- else
- c = topattern(c,true,true)
- for k, v in next, data do
- if find(k,c) then
- v.state = value
- end
- end
- end
- end
- end
-end
-
-function logs.disable(category,value)
- setblocked(category,value == nil and true or value)
-end
-
-function logs.enable(category)
- setblocked(category,false)
-end
-
-function logs.categories()
- return table.sortedkeys(data)
-end
-
-function logs.show()
- local n, c, s, max = 0, 0, 0, 0
- for category, v in table.sortedpairs(data) do
- n = n + 1
- local state = v.state
- local reporters = v.reporters
- local nc = #category
- if nc > c then
- c = nc
- end
- for subcategory, _ in next, reporters do
- local ns = #subcategory
- if ns > c then
- s = ns
- end
- local m = nc + ns
- if m > max then
- max = m
- end
- end
- local subcategories = concat(table.sortedkeys(reporters),", ")
- if state == true then
- state = "disabled"
- elseif state == false then
- state = "enabled"
- else
- state = "unknown"
- end
- -- no new here
- report("logging","category %a, subcategories %a, state %a",category,subcategories,state)
- end
- report("logging","categories: %s, max category: %s, max subcategory: %s, max combined: %s",n,c,s,max)
-end
-
-local delayed_reporters = { }
-
-setmetatableindex(delayed_reporters,function(t,k)
- local v = logs.reporter(k.name)
- t[k] = v
- return v
-end)
-
-function utilities.setters.report(setter,...)
- delayed_reporters[setter](...)
-end
-
-directives.register("logs.blocked", function(v)
- setblocked(v,true)
-end)
-
-directives.register("logs.target", function(v)
- settarget(v)
-end)
-
--- tex specific loggers (might move elsewhere)
-
-local report_pages = logs.reporter("pages") -- not needed but saves checking when we grep for it
-
-local real, user, sub
-
-function logs.start_page_number()
- real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
--- real, user, sub = 0, 0, 0
-end
-
-local timing = false
-local starttime = nil
-local lasttime = nil
-
-trackers.register("pages.timing", function(v) -- only for myself (diagnostics)
- starttime = os.clock()
- timing = true
-end)
-
-function logs.stop_page_number() -- the first page can includes the initialization so we omit this in average
- if timing then
- local elapsed, average
- local stoptime = os.clock()
- if not lasttime or real < 2 then
- elapsed = stoptime
- average = stoptime
- starttime = stoptime
- else
- elapsed = stoptime - lasttime
- average = (stoptime - starttime) / (real - 1)
- end
- lasttime = stoptime
- if real <= 0 then
- report_pages("flushing page, time %0.04f / %0.04f",elapsed,average)
- elseif user <= 0 then
- report_pages("flushing realpage %s, time %0.04f / %0.04f",real,elapsed,average)
- elseif sub <= 0 then
- report_pages("flushing realpage %s, userpage %s, time %0.04f / %0.04f",real,user,elapsed,average)
- else
- report_pages("flushing realpage %s, userpage %s, subpage %s, time %0.04f / %0.04f",real,user,sub,elapsed,average)
- end
- else
- if real <= 0 then
- report_pages("flushing page")
- elseif user <= 0 then
- report_pages("flushing realpage %s",real)
- elseif sub <= 0 then
- report_pages("flushing realpage %s, userpage %s",real,user)
- else
- report_pages("flushing realpage %s, userpage %s, subpage %s",real,user,sub)
- end
- end
- logs.flush()
-end
-
--- we don't have show_open and show_close callbacks yet
-
-local report_files = logs.reporter("files")
-local nesting = 0
-local verbose = false
-local hasscheme = url.hasscheme
-
-function logs.show_open(name)
- -- if hasscheme(name) ~= "virtual" then
- -- if verbose then
- -- nesting = nesting + 1
- -- report_files("level %s, opening %s",nesting,name)
- -- else
- -- write(formatters["(%s"](name)) -- tex adds a space
- -- end
- -- end
-end
-
-function logs.show_close(name)
- -- if hasscheme(name) ~= "virtual" then
- -- if verbose then
- -- report_files("level %s, closing %s",nesting,name)
- -- nesting = nesting - 1
- -- else
- -- write(")") -- tex adds a space
- -- end
- -- end
-end
-
-function logs.show_load(name)
- -- if hasscheme(name) ~= "virtual" then
- -- if verbose then
- -- report_files("level %s, loading %s",nesting+1,name)
- -- else
- -- write(formatters["(%s)"](name))
- -- end
- -- end
-end
-
--- there may be scripts out there using this:
-
-local simple = logs.reporter("comment")
-
-logs.simple = simple
-logs.simpleline = simple
-
--- obsolete
-
-function logs.setprogram () end -- obsolete
-function logs.extendbanner() end -- obsolete
-function logs.reportlines () end -- obsolete
-function logs.reportbanner() end -- obsolete
-function logs.reportline () end -- obsolete
-function logs.simplelines () end -- obsolete
-function logs.help () end -- obsolete
-
--- applications
-
--- local function reportlines(t,str)
--- if str then
--- for line in gmatch(str,"([^\n\r]*)[\n\r]") do
--- t.report(line)
--- end
--- end
--- end
-
-local Carg, C, lpegmatch = lpeg.Carg, lpeg.C, lpeg.match
-local p_newline = lpeg.patterns.newline
-
-local linewise = (
- Carg(1) * C((1-p_newline)^1) / function(t,s) t.report(s) end
- + Carg(1) * p_newline^2 / function(t) t.report() end
- + p_newline
-)^1
-
-local function reportlines(t,str)
- if str then
- lpegmatch(linewise,str,1,t)
- end
-end
-
-local function reportbanner(t)
- local banner = t.banner
- if banner then
- t.report(banner)
- t.report()
- end
-end
-
-local function reportversion(t)
- local banner = t.banner
- if banner then
- t.report(banner)
- end
-end
-
-local function reporthelp(t,...)
- local helpinfo = t.helpinfo
- if type(helpinfo) == "string" then
- reportlines(t,helpinfo)
- elseif type(helpinfo) == "table" then
- for i=1,select("#",...) do
- reportlines(t,t.helpinfo[select(i,...)])
- if i < n then
- t.report()
- end
- end
- end
-end
-
-local function reportinfo(t)
- t.report()
- reportlines(t,t.moreinfo)
-end
-
-local function reportexport(t,method)
- report(t.helpinfo)
-end
-
-local reporters = {
- lines = reportlines, -- not to be overloaded
- banner = reportbanner,
- version = reportversion,
- help = reporthelp,
- info = reportinfo,
- export = reportexport,
-}
-
-local exporters = {
- -- empty
-}
-
-logs.reporters = reporters
-logs.exporters = exporters
-
-function logs.application(t)
- t.name = t.name or "unknown"
- t.banner = t.banner
- t.moreinfo = moreinfo
- t.report = logs.reporter(t.name)
- t.help = function(...)
- reporters.banner(t)
- reporters.help(t,...)
- reporters.info(t)
- end
- t.export = function(...)
- reporters.export(t,...)
- end
- t.identify = function()
- reporters.banner(t)
- end
- t.version = function()
- reporters.version(t)
- end
- return t
-end
-
--- somewhat special .. will be redone (already a better solution in place in lmx)
-
--- logging to a file
-
--- local syslogname = "oeps.xxx"
---
--- for i=1,10 do
--- logs.system(syslogname,"context","test","fonts","font %s recached due to newer version (%s)","blabla","123")
--- end
-
-function logs.system(whereto,process,jobname,category,...)
- local message = formatters["%s %s => %s => %s => %s\r"](os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...))
- for i=1,10 do
- local f = io.open(whereto,"a") -- we can consider keeping the file open
- if f then
- f:write(message)
- f:close()
- break
- else
- sleep(0.1)
- end
- end
-end
-
-local report_system = logs.reporter("system","logs")
-
-function logs.obsolete(old,new)
- local o = loadstring("return " .. new)()
- if type(o) == "function" then
- return function(...)
- report_system("function %a is obsolete, use %a",old,new)
- loadstring(old .. "=" .. new .. " return ".. old)()(...)
- end
- elseif type(o) == "table" then
- local t, m = { }, { }
- m.__index = function(t,k)
- report_system("table %a is obsolete, use %a",old,new)
- m.__index, m.__newindex = o, o
- return o[k]
- end
- m.__newindex = function(t,k,v)
- report_system("table %a is obsolete, use %a",old,new)
- m.__index, m.__newindex = o, o
- o[k] = v
- end
- if libraries then
- libraries.obsolete[old] = t -- true
- end
- setmetatable(t,m)
- return t
- end
-end
-
-if utilities then
- utilities.report = report_system
-end
-
-if tex and tex.error then
- function logs.texerrormessage(...) -- for the moment we put this function here
- tex.error(format(...), { })
- end
-else
- function logs.texerrormessage(...)
- print(format(...))
- end
-end
-
--- this is somewhat slower but prevents out-of-order messages when print is mixed
--- with texio.write
-
-io.stdout:setvbuf('no')
-io.stderr:setvbuf('no')
-
--- windows: > nul 2>&1
--- unix : > null 2>&1
-
-if package.helpers.report then
- package.helpers.report = logs.reporter("package loader") -- when used outside mtxrun
-end
+if not modules then modules = { } end modules ['trac-log'] = { + version = 1.001, + comment = "companion to trac-log.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- if tex and (tex.jobname or tex.formatname) then +-- +-- -- quick hack, awaiting speedup in engine (8 -> 6.4 sec for --make with console2) +-- -- still needed for luajittex +-- +-- local texio_write_nl = texio.write_nl +-- local texio_write = texio.write +-- local io_write = io.write + +-- local write_nl = function(target,...) +-- if not io_write then +-- io_write = io.write +-- end +-- if target == "term and log" then +-- texio_write_nl("log",...) +-- texio_write_nl("term","") +-- io_write(...) +-- elseif target == "log" then +-- texio_write_nl("log",...) +-- elseif target == "term" then +-- texio_write_nl("term","") +-- io_write(...) +-- else +-- texio_write_nl("log",target,...) +-- texio_write_nl("term","") +-- io_write(target,...) +-- end +-- end + +-- local write = function(target,...) +-- if not io_write then +-- io_write = io.write +-- end +-- if target == "term and log" then +-- texio_write("log",...) +-- io_write(...) +-- elseif target == "log" then +-- texio_write("log",...) +-- elseif target == "term" then +-- io_write(...) +-- else +-- texio_write("log",target,...) +-- io_write(target,...) +-- end +-- end + +-- texio.write = write +-- texio.write_nl = write_nl +-- +-- else +-- +-- -- texlua or just lua +-- +-- end + +-- todo: less categories, more subcategories (e.g. nodes) +-- todo: split into basics and ctx specific + +local write_nl, write = texio and texio.write_nl or print, texio and texio.write or io.write +local format, gmatch, find = string.format, string.gmatch, string.find +local concat, insert, remove = table.concat, table.insert, table.remove +local topattern = string.topattern +local texcount = tex and tex.count +local next, type, select = next, type, select +local utfchar = utf.char + +local setmetatableindex = table.setmetatableindex +local formatters = string.formatters + +--[[ldx-- +<p>This is a prelude to a more extensive logging module. We no longer +provide <l n='xml'/> based logging as parsing is relatively easy anyway.</p> +--ldx]]-- + +logs = logs or { } +local logs = logs + +local moreinfo = [[ +More information about ConTeXt and the tools that come with it can be found at: +]] .. "\n" .. [[ +maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context +webpage : http://www.pragma-ade.nl / http://tex.aanhet.net +wiki : http://contextgarden.net +]] + +-- -- we extend the formatters: +-- +-- function utilities.strings.unichr(s) return "U+" .. format("%05X",s) .. " (" .. utfchar(s) .. ")" end +-- function utilities.strings.chruni(s) return utfchar(s) .. " (U+" .. format("%05X",s) .. ")" end +-- +-- utilities.strings.formatters.add ( +-- string.formatters, "uni", +-- [[unichr(%s)]], +-- [[local unichr = utilities.strings.unichr]] +-- ) +-- +-- utilities.strings.formatters.add ( +-- string.formatters, "chr", +-- [[chruni(%s)]], +-- [[local chruni = utilities.strings.chruni]] +-- ) + +utilities.strings.formatters.add ( + formatters, "unichr", + [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]] +) + +utilities.strings.formatters.add ( + formatters, "chruni", + [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]] +) + +-- print(formatters["Missing character %!chruni! in font."](234)) +-- print(formatters["Missing character %!unichr! in font."](234)) + +-- basic loggers + +local function ignore() end + +setmetatableindex(logs, function(t,k) t[k] = ignore ; return ignore end) + +local report, subreport, status, settarget, setformats, settranslations + +local direct, subdirect, writer, pushtarget, poptarget + +if tex and (tex.jobname or tex.formatname) then + + -- local format = string.formatter + + local valueiskey = { __index = function(t,k) t[k] = k return k end } -- will be helper + + local target = "term and log" + + logs.flush = io.flush + + local formats = { } setmetatable(formats, valueiskey) + local translations = { } setmetatable(translations,valueiskey) + + writer = function(...) + write_nl(target,...) + end + + newline = function() + write_nl(target,"\n") + end + + local f_one = formatters["%-15s > %s\n"] + local f_two = formatters["%-15s >\n"] + + -- we can use formatters but best check for % then because for simple messages + -- we con't want this overhead for single messages (not that there are that + -- many; we could have a special weak table) + + report = function(a,b,c,...) + if c then + write_nl(target,f_one(translations[a],formatters[formats[b]](c,...))) + elseif b then + write_nl(target,f_one(translations[a],formats[b])) + elseif a then + write_nl(target,f_two(translations[a])) + else + write_nl(target,"\n") + end + end + + local f_one = formatters["%-15s > %s"] + local f_two = formatters["%-15s >"] + + direct = function(a,b,c,...) + if c then + return f_one(translations[a],formatters[formats[b]](c,...)) + elseif b then + return f_one(translations[a],formats[b]) + elseif a then + return f_two(translations[a]) + else + return "" + end + end + + local f_one = formatters["%-15s > %s > %s\n"] + local f_two = formatters["%-15s > %s >\n"] + + subreport = function(a,s,b,c,...) + if c then + write_nl(target,f_one(translations[a],translations[s],formatters[formats[b]](c,...))) + elseif b then + write_nl(target,f_one(translations[a],translations[s],formats[b])) + elseif a then + write_nl(target,f_two(translations[a],translations[s])) + else + write_nl(target,"\n") + end + end + + local f_one = formatters["%-15s > %s > %s"] + local f_two = formatters["%-15s > %s >"] + + subdirect = function(a,s,b,c,...) + if c then + return f_one(translations[a],translations[s],formatters[formats[b]](c,...)) + elseif b then + return f_one(translations[a],translations[s],formats[b]) + elseif a then + return f_two(translations[a],translations[s]) + else + return "" + end + end + + local f_one = formatters["%-15s : %s\n"] + local f_two = formatters["%-15s :\n"] + + status = function(a,b,c,...) + if c then + write_nl(target,f_one(translations[a],formatters[formats[b]](c,...))) + elseif b then + write_nl(target,f_one(translations[a],formats[b])) + elseif a then + write_nl(target,f_two(translations[a])) + else + write_nl(target,"\n") + end + end + + local targets = { + logfile = "log", + log = "log", + file = "log", + console = "term", + terminal = "term", + both = "term and log", + } + + settarget = function(whereto) + target = targets[whereto or "both"] or targets.both + if target == "term" or target == "term and log" then + logs.flush = io.flush + else + logs.flush = ignore + end + end + + local stack = { } + + pushtarget = function(newtarget) + insert(stack,target) + settarget(newtarget) + end + + poptarget = function() + if #stack > 0 then + settarget(remove(stack)) + end + end + + setformats = function(f) + formats = f + end + + settranslations = function(t) + translations = t + end + +else + + logs.flush = ignore + + writer = write_nl + + newline = function() + write_nl("\n") + end + + local f_one = formatters["%-15s | %s"] + local f_two = formatters["%-15s |"] + + report = function(a,b,c,...) + if c then + write_nl(f_one(a,formatters[b](c,...))) + elseif b then + write_nl(f_one(a,b)) + elseif a then + write_nl(f_two(a)) + else + write_nl("") + end + end + + local f_one = formatters["%-15s | %s | %s"] + local f_two = formatters["%-15s | %s |"] + + subreport = function(a,sub,b,c,...) + if c then + write_nl(f_one(a,sub,formatters[b](c,...))) + elseif b then + write_nl(f_one(a,sub,b)) + elseif a then + write_nl(f_two(a,sub)) + else + write_nl("") + end + end + + local f_one = formatters["%-15s : %s\n"] + local f_two = formatters["%-15s :\n"] + + status = function(a,b,c,...) -- not to be used in lua anyway + if c then + write_nl(f_one(a,formatters[b](c,...))) + elseif b then + write_nl(f_one(a,b)) -- b can have %'s + elseif a then + write_nl(f_two(a)) + else + write_nl("\n") + end + end + + direct = ignore + subdirect = ignore + + settarget = ignore + pushtarget = ignore + poptarget = ignore + setformats = ignore + settranslations = ignore + +end + +logs.report = report +logs.subreport = subreport +logs.status = status +logs.settarget = settarget +logs.pushtarget = pushtarget +logs.poptarget = poptarget +logs.setformats = setformats +logs.settranslations = settranslations + +logs.direct = direct +logs.subdirect = subdirect +logs.writer = writer +logs.newline = newline + +-- installer + +-- todo: renew (un) locks when a new one is added and wildcard + +local data, states = { }, nil + +function logs.reporter(category,subcategory) + local logger = data[category] + if not logger then + local state = false + if states == true then + state = true + elseif type(states) == "table" then + for c, _ in next, states do + if find(category,c) then + state = true + break + end + end + end + logger = { + reporters = { }, + state = state, + } + data[category] = logger + end + local reporter = logger.reporters[subcategory or "default"] + if not reporter then + if subcategory then + reporter = function(...) + if not logger.state then + subreport(category,subcategory,...) + end + end + logger.reporters[subcategory] = reporter + else + local tag = category + reporter = function(...) + if not logger.state then + report(category,...) + end + end + logger.reporters.default = reporter + end + end + return reporter +end + +logs.new = logs.reporter -- for old times sake + +-- context specicific: this ends up in the macro stream + +local ctxreport = logs.writer + +function logs.setmessenger(m) + ctxreport = m +end + +function logs.messenger(category,subcategory) + -- we need to avoid catcode mess (todo: fast context) + if subcategory then + return function(...) + ctxreport(subdirect(category,subcategory,...)) + end + else + return function(...) + ctxreport(direct(category,...)) + end + end +end + +-- so far + +local function setblocked(category,value) + if category == true then + -- lock all + category, value = "*", true + elseif category == false then + -- unlock all + category, value = "*", false + elseif value == nil then + -- lock selective + value = true + end + if category == "*" then + states = value + for k, v in next, data do + v.state = value + end + else + states = utilities.parsers.settings_to_hash(category) + for c, _ in next, states do + if data[c] then + v.state = value + else + c = topattern(c,true,true) + for k, v in next, data do + if find(k,c) then + v.state = value + end + end + end + end + end +end + +function logs.disable(category,value) + setblocked(category,value == nil and true or value) +end + +function logs.enable(category) + setblocked(category,false) +end + +function logs.categories() + return table.sortedkeys(data) +end + +function logs.show() + local n, c, s, max = 0, 0, 0, 0 + for category, v in table.sortedpairs(data) do + n = n + 1 + local state = v.state + local reporters = v.reporters + local nc = #category + if nc > c then + c = nc + end + for subcategory, _ in next, reporters do + local ns = #subcategory + if ns > c then + s = ns + end + local m = nc + ns + if m > max then + max = m + end + end + local subcategories = concat(table.sortedkeys(reporters),", ") + if state == true then + state = "disabled" + elseif state == false then + state = "enabled" + else + state = "unknown" + end + -- no new here + report("logging","category %a, subcategories %a, state %a",category,subcategories,state) + end + report("logging","categories: %s, max category: %s, max subcategory: %s, max combined: %s",n,c,s,max) +end + +local delayed_reporters = { } + +setmetatableindex(delayed_reporters,function(t,k) + local v = logs.reporter(k.name) + t[k] = v + return v +end) + +function utilities.setters.report(setter,...) + delayed_reporters[setter](...) +end + +directives.register("logs.blocked", function(v) + setblocked(v,true) +end) + +directives.register("logs.target", function(v) + settarget(v) +end) + +-- tex specific loggers (might move elsewhere) + +local report_pages = logs.reporter("pages") -- not needed but saves checking when we grep for it + +local real, user, sub + +function logs.start_page_number() + real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno +-- real, user, sub = 0, 0, 0 +end + +local timing = false +local starttime = nil +local lasttime = nil + +trackers.register("pages.timing", function(v) -- only for myself (diagnostics) + starttime = os.clock() + timing = true +end) + +function logs.stop_page_number() -- the first page can includes the initialization so we omit this in average + if timing then + local elapsed, average + local stoptime = os.clock() + if not lasttime or real < 2 then + elapsed = stoptime + average = stoptime + starttime = stoptime + else + elapsed = stoptime - lasttime + average = (stoptime - starttime) / (real - 1) + end + lasttime = stoptime + if real <= 0 then + report_pages("flushing page, time %0.04f / %0.04f",elapsed,average) + elseif user <= 0 then + report_pages("flushing realpage %s, time %0.04f / %0.04f",real,elapsed,average) + elseif sub <= 0 then + report_pages("flushing realpage %s, userpage %s, time %0.04f / %0.04f",real,user,elapsed,average) + else + report_pages("flushing realpage %s, userpage %s, subpage %s, time %0.04f / %0.04f",real,user,sub,elapsed,average) + end + else + if real <= 0 then + report_pages("flushing page") + elseif user <= 0 then + report_pages("flushing realpage %s",real) + elseif sub <= 0 then + report_pages("flushing realpage %s, userpage %s",real,user) + else + report_pages("flushing realpage %s, userpage %s, subpage %s",real,user,sub) + end + end + logs.flush() +end + +-- we don't have show_open and show_close callbacks yet + +local report_files = logs.reporter("files") +local nesting = 0 +local verbose = false +local hasscheme = url.hasscheme + +function logs.show_open(name) + -- if hasscheme(name) ~= "virtual" then + -- if verbose then + -- nesting = nesting + 1 + -- report_files("level %s, opening %s",nesting,name) + -- else + -- write(formatters["(%s"](name)) -- tex adds a space + -- end + -- end +end + +function logs.show_close(name) + -- if hasscheme(name) ~= "virtual" then + -- if verbose then + -- report_files("level %s, closing %s",nesting,name) + -- nesting = nesting - 1 + -- else + -- write(")") -- tex adds a space + -- end + -- end +end + +function logs.show_load(name) + -- if hasscheme(name) ~= "virtual" then + -- if verbose then + -- report_files("level %s, loading %s",nesting+1,name) + -- else + -- write(formatters["(%s)"](name)) + -- end + -- end +end + +-- there may be scripts out there using this: + +local simple = logs.reporter("comment") + +logs.simple = simple +logs.simpleline = simple + +-- obsolete + +function logs.setprogram () end -- obsolete +function logs.extendbanner() end -- obsolete +function logs.reportlines () end -- obsolete +function logs.reportbanner() end -- obsolete +function logs.reportline () end -- obsolete +function logs.simplelines () end -- obsolete +function logs.help () end -- obsolete + +-- applications + +-- local function reportlines(t,str) +-- if str then +-- for line in gmatch(str,"([^\n\r]*)[\n\r]") do +-- t.report(line) +-- end +-- end +-- end + +local Carg, C, lpegmatch = lpeg.Carg, lpeg.C, lpeg.match +local p_newline = lpeg.patterns.newline + +local linewise = ( + Carg(1) * C((1-p_newline)^1) / function(t,s) t.report(s) end + + Carg(1) * p_newline^2 / function(t) t.report() end + + p_newline +)^1 + +local function reportlines(t,str) + if str then + lpegmatch(linewise,str,1,t) + end +end + +local function reportbanner(t) + local banner = t.banner + if banner then + t.report(banner) + t.report() + end +end + +local function reportversion(t) + local banner = t.banner + if banner then + t.report(banner) + end +end + +local function reporthelp(t,...) + local helpinfo = t.helpinfo + if type(helpinfo) == "string" then + reportlines(t,helpinfo) + elseif type(helpinfo) == "table" then + for i=1,select("#",...) do + reportlines(t,t.helpinfo[select(i,...)]) + if i < n then + t.report() + end + end + end +end + +local function reportinfo(t) + t.report() + reportlines(t,t.moreinfo) +end + +local function reportexport(t,method) + report(t.helpinfo) +end + +local reporters = { + lines = reportlines, -- not to be overloaded + banner = reportbanner, + version = reportversion, + help = reporthelp, + info = reportinfo, + export = reportexport, +} + +local exporters = { + -- empty +} + +logs.reporters = reporters +logs.exporters = exporters + +function logs.application(t) + t.name = t.name or "unknown" + t.banner = t.banner + t.moreinfo = moreinfo + t.report = logs.reporter(t.name) + t.help = function(...) + reporters.banner(t) + reporters.help(t,...) + reporters.info(t) + end + t.export = function(...) + reporters.export(t,...) + end + t.identify = function() + reporters.banner(t) + end + t.version = function() + reporters.version(t) + end + return t +end + +-- somewhat special .. will be redone (already a better solution in place in lmx) + +-- logging to a file + +-- local syslogname = "oeps.xxx" +-- +-- for i=1,10 do +-- logs.system(syslogname,"context","test","fonts","font %s recached due to newer version (%s)","blabla","123") +-- end + +function logs.system(whereto,process,jobname,category,...) + local message = formatters["%s %s => %s => %s => %s\r"](os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...)) + for i=1,10 do + local f = io.open(whereto,"a") -- we can consider keeping the file open + if f then + f:write(message) + f:close() + break + else + sleep(0.1) + end + end +end + +local report_system = logs.reporter("system","logs") + +function logs.obsolete(old,new) + local o = loadstring("return " .. new)() + if type(o) == "function" then + return function(...) + report_system("function %a is obsolete, use %a",old,new) + loadstring(old .. "=" .. new .. " return ".. old)()(...) + end + elseif type(o) == "table" then + local t, m = { }, { } + m.__index = function(t,k) + report_system("table %a is obsolete, use %a",old,new) + m.__index, m.__newindex = o, o + return o[k] + end + m.__newindex = function(t,k,v) + report_system("table %a is obsolete, use %a",old,new) + m.__index, m.__newindex = o, o + o[k] = v + end + if libraries then + libraries.obsolete[old] = t -- true + end + setmetatable(t,m) + return t + end +end + +if utilities then + utilities.report = report_system +end + +if tex and tex.error then + function logs.texerrormessage(...) -- for the moment we put this function here + tex.error(format(...), { }) + end +else + function logs.texerrormessage(...) + print(format(...)) + end +end + +-- this is somewhat slower but prevents out-of-order messages when print is mixed +-- with texio.write + +io.stdout:setvbuf('no') +io.stderr:setvbuf('no') + +-- windows: > nul 2>&1 +-- unix : > null 2>&1 + +if package.helpers.report then + package.helpers.report = logs.reporter("package loader") -- when used outside mtxrun +end |