summaryrefslogtreecommitdiff
path: root/tex
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2012-09-21 20:58:00 +0200
committerHans Hagen <pragma@wxs.nl>2012-09-21 20:58:00 +0200
commit0c505920129b0e27f00c7e7fa328c182b1576d67 (patch)
tree3014cc437d27e2f51e37b5093ea80cf5a5b381f9 /tex
parentc60c6b0d5a3cefd662b44d08faecda1caf7e6cdc (diff)
downloadcontext-0c505920129b0e27f00c7e7fa328c182b1576d67.tar.gz
beta 2012.09.21 20:58
Diffstat (limited to 'tex')
-rw-r--r--tex/context/base/cont-new.mkii2
-rw-r--r--tex/context/base/cont-new.mkiv2
-rw-r--r--tex/context/base/context-version.pdfbin4141 -> 4140 bytes
-rw-r--r--tex/context/base/context-version.pngbin106457 -> 106731 bytes
-rw-r--r--tex/context/base/context.mkii2
-rw-r--r--tex/context/base/context.mkiv2
-rw-r--r--tex/context/base/core-dat.lua60
-rw-r--r--tex/context/base/data-lua.lua254
-rw-r--r--tex/context/base/data-pre.lua2
-rw-r--r--tex/context/base/data-res.lua2
-rw-r--r--tex/context/base/l-os.lua47
-rw-r--r--tex/context/base/l-table.lua28
-rw-r--r--tex/context/base/luat-env.lua2
-rw-r--r--tex/context/base/m-pstricks.lua8
-rw-r--r--tex/context/base/mult-low.lua2
-rw-r--r--tex/context/base/status-files.pdfbin24570 -> 24577 bytes
-rw-r--r--tex/context/base/status-lua.pdfbin195041 -> 195092 bytes
-rw-r--r--tex/context/base/util-sql-loggers.lua277
-rw-r--r--tex/context/base/util-sql-sessions.lua349
-rw-r--r--tex/context/base/util-sql-tickets.lua698
-rw-r--r--tex/context/base/util-sql.lua267
-rw-r--r--tex/context/base/util-tpl.lua36
-rw-r--r--tex/generic/context/luatex/luatex-fonts-merged.lua30
23 files changed, 1840 insertions, 230 deletions
diff --git a/tex/context/base/cont-new.mkii b/tex/context/base/cont-new.mkii
index 2e90a2dc9..d7edc921c 100644
--- a/tex/context/base/cont-new.mkii
+++ b/tex/context/base/cont-new.mkii
@@ -11,7 +11,7 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-\newcontextversion{2012.09.16 23:18}
+\newcontextversion{2012.09.21 20:58}
%D This file is loaded at runtime, thereby providing an
%D excellent place for hacks, patches, extensions and new
diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv
index cc6342b05..2f90130c7 100644
--- a/tex/context/base/cont-new.mkiv
+++ b/tex/context/base/cont-new.mkiv
@@ -11,7 +11,7 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-\newcontextversion{2012.09.16 23:18}
+\newcontextversion{2012.09.21 20:58}
%D This file is loaded at runtime, thereby providing an excellent place for
%D hacks, patches, extensions and new features.
diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf
index 7db72b855..e232cfeb6 100644
--- a/tex/context/base/context-version.pdf
+++ b/tex/context/base/context-version.pdf
Binary files differ
diff --git a/tex/context/base/context-version.png b/tex/context/base/context-version.png
index 98253d101..048d04143 100644
--- a/tex/context/base/context-version.png
+++ b/tex/context/base/context-version.png
Binary files differ
diff --git a/tex/context/base/context.mkii b/tex/context/base/context.mkii
index 2582f1f9f..0aab24aad 100644
--- a/tex/context/base/context.mkii
+++ b/tex/context/base/context.mkii
@@ -20,7 +20,7 @@
%D your styles an modules.
\edef\contextformat {\jobname}
-\edef\contextversion{2012.09.16 23:18}
+\edef\contextversion{2012.09.21 20:58}
%D For those who want to use this:
diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv
index 52c4d897e..d19d90a93 100644
--- a/tex/context/base/context.mkiv
+++ b/tex/context/base/context.mkiv
@@ -25,7 +25,7 @@
%D up and the dependencies are more consistent.
\edef\contextformat {\jobname}
-\edef\contextversion{2012.09.16 23:18}
+\edef\contextversion{2012.09.21 20:58}
%D For those who want to use this:
diff --git a/tex/context/base/core-dat.lua b/tex/context/base/core-dat.lua
index 071a3fe0b..879ff6130 100644
--- a/tex/context/base/core-dat.lua
+++ b/tex/context/base/core-dat.lua
@@ -11,7 +11,7 @@ if not modules then modules = { } end modules ['core-dat'] = {
replaces the twopass data mechanism.</p>
--ldx]]--
-local tonumber = tonumber
+local tonumber, type = tonumber, type
local context, commands = context, commands
@@ -69,7 +69,12 @@ local function setdata(settings)
local tag = settings.tag
local data = settings.data
local list = tobesaved[name]
- data = settings_to_hash(data) or { }
+ if settings.convert and type(data) == "string" then
+ data = settings_to_hash(data)
+ end
+ if type(data) ~= "table" then
+ data = { data = settings.data }
+ end
if not tag then
tag = #list + 1
else
@@ -109,25 +114,30 @@ end
function datasets.getdata(name,tag,key,default)
local t = collected[name]
- if t then
+ if t == nil then
+ if trace_datasets then
+ report_dataset("unknown: name %s",name)
+ end
+ elseif type(t) ~= "table" then
+ return t
+ else
t = t[tag] or t[tonumber(tag)]
- if t then
- if key then
- return t[key] or default
- else
- return t
+ if not t then
+ if trace_datasets then
+ report_dataset("unknown: name %s, tag %s",name,tag)
end
- elseif trace_datasets then
- report_dataset("unknown: name %s, tag %s",name,tag)
+ elseif key then
+ return t[key] or default
+ else
+ return t
end
- elseif trace_datasets then
- report_dataset("unknown: name %s",name)
end
return default
end
function commands.setdataset(settings)
- local name, tag, data = setdata(settings)
+ settings.convert = true
+ local name, tag = setdata(settings)
if settings.delay ~= v_yes then
--
elseif type(tag) == "number" then
@@ -139,11 +149,25 @@ end
function commands.datasetvariable(name,tag,key)
local t = collected[name]
- t = t and (t[tag] or t[tonumber(tag)])
- if t then
- local s = t[key]
- if s then
- context(s)
+ if t == nil then
+ if trace_datasets then
+ report_dataset("unknown: name %s (not passed to tex)",name)
+ end
+ elseif type(t) ~= "table" then
+ context(tostring(t))
+ else
+ t = t and (t[tag] or t[tonumber(tag)])
+ if not t then
+ if trace_datasets then
+ report_dataset("unknown: name %s with tag %s (not passed to tex)",name,tag)
+ end
+ elseif type(t) ~= "table" then
+ local s = t[key]
+ if type(s) ~= "table" then
+ context(tostring(s))
+ elseif trace_datasets then
+ report_dataset("table: name %s, tag %s (not passed to tex)",name,tag)
+ end
end
end
end
diff --git a/tex/context/base/data-lua.lua b/tex/context/base/data-lua.lua
index ab762f3d4..fec5856ea 100644
--- a/tex/context/base/data-lua.lua
+++ b/tex/context/base/data-lua.lua
@@ -6,9 +6,15 @@ if not modules then modules = { } end modules ['data-lua'] = {
license = "see context related readme files"
}
--- some loading stuff ... we might move this one to slot 2 depending
--- on the developments (the loaders must not trigger kpse); we could
--- of course use a more extensive lib path spec
+-- We overload the regular loader. We do so because we operate mostly in
+-- tds and use our own loader code. Alternatively we could use a more
+-- extensive definition of package.path and package.cpath but even then
+-- we're not done. Also, we now have better tracing.
+--
+-- -- local mylib = require("libtest")
+-- -- local mysql = require("luasql.mysql")
+
+local concat = table.concat
local trace_libraries = false
@@ -18,160 +24,178 @@ trackers.register("resolvers.locating", function(v) trace_libraries = v end)
local report_libraries = logs.reporter("resolvers","libraries")
local gsub, insert = string.gsub, table.insert
+local P, Cs, lpegmatch = lpeg.P, lpeg.Cs, lpeg.match
local unpack = unpack or table.unpack
+local is_readable = file.is_readable
local resolvers, package = resolvers, package
--- local libformats = { 'luatexlibs', 'tex', 'texmfscripts', 'othertextfiles' }
-local libformats = { 'lua', 'tex' }
-local clibformats = { 'lib' }
+local libsuffixes = { 'tex', 'lua' }
+local clibsuffixes = { 'lib' }
+local libformats = { 'TEXINPUTS', 'LUAINPUTS' }
+local clibformats = { 'CLUAINPUTS' }
+
+local libpaths = nil
+local clibpaths = nil
+local libhash = { }
+local clibhash = { }
+local libextras = { }
+local clibextras = { }
-local _path_, libpaths, _cpath_, clibpaths
+local pattern = Cs(P("!")^0 / "" * (P("/") * P(-1) / "/" + P("/")^1 / "/" + 1)^0)
+
+local function cleanpath(path) --hm, don't we have a helper for this?
+ return resolvers.resolve(lpegmatch(pattern,path))
+end
-function package.libpaths()
- if not _path_ or package.path ~= _path_ then
- _path_ = package.path
- libpaths = file.splitpath(_path_,";")
+local function getlibpaths()
+ if not libpaths then
+ libpaths = { }
+ for i=1,#libformats do
+ local paths = resolvers.expandedpathlistfromvariable(libformats[i])
+ for i=1,#paths do
+ local path = cleanpath(paths[i])
+ if not libhash[path] then
+ libpaths[#libpaths+1] = path
+ libhash[path] = true
+ end
+ end
+ end
end
return libpaths
end
-function package.clibpaths()
- if not _cpath_ or package.cpath ~= _cpath_ then
- _cpath_ = package.cpath
- clibpaths = file.splitpath(_cpath_,";")
+local function getclibpaths()
+ if not clibpaths then
+ clibpaths = { }
+ for i=1,#clibformats do
+ local paths = resolvers.expandedpathlistfromvariable(clibformats[i])
+ for i=1,#paths do
+ local path = cleanpath(paths[i])
+ if not clibhash[path] then
+ clibpaths[#clibpaths+1] = path
+ clibhash[path] = true
+ end
+ end
+ end
end
return clibpaths
end
-local function thepath(...)
- local t = { ... } t[#t+1] = "?.lua"
- local path = file.join(unpack(t))
- if trace_libraries then
- report_libraries("! appending '%s' to 'package.path'",path)
+package.libpaths = getlibpaths
+package.clibpaths = getclibpaths
+
+function package.extralibpath(...)
+ local paths = { ... }
+ for i=1,#paths do
+ local path = cleanpath(paths[i])
+ if not libhash[path] then
+ if trace_libraries then
+ report_libraries("! extra lua path '%s'",path)
+ end
+ libextras[#libextras+1] = path
+ libpaths[#libpaths +1] = path
+ end
end
- return path
end
-local p_libpaths, a_libpaths = { }, { }
-
-function package.appendtolibpath(...)
- insert(a_libpath,thepath(...))
+function package.extraclibpath(...)
+ local paths = { ... }
+ for i=1,#paths do
+ local path = cleanpath(paths[i])
+ if not clibhash[path] then
+ if trace_libraries then
+ report_libraries("! extra lib path '%s'",path)
+ end
+ clibextras[#clibextras+1] = path
+ clibpaths[#clibpaths +1] = path
+ end
+ end
end
-function package.prependtolibpath(...)
- insert(p_libpaths,1,thepath(...))
+if not package.loaders[-2] then
+ -- use package-path and package-cpath
+ package.loaders[-2] = package.loaders[2]
end
--- beware, we need to return a loadfile result !
+local function loadedaslib(resolved,rawname)
+ return package.loadlib(resolved,"luaopen_" .. gsub(rawname,"%.","_"))
+end
-local function loaded(libpaths,name,simple)
- for i=1,#libpaths do -- package.path, might become option
- local libpath = libpaths[i]
- local resolved = gsub(libpath,"%?",simple)
- if trace_libraries then -- more detail
- report_libraries("! checking for '%s' on 'package.path': '%s' => '%s'",simple,libpath,resolved)
- end
- if file.is_readable(resolved) then
- if trace_libraries then
- report_libraries("! lib '%s' located via 'package.path': '%s'",name,resolved)
- end
- return loadfile(resolved)
- end
+local function loadedbylua(name)
+ if trace_libraries then
+ report_libraries("! locating %q using normal loader",name)
end
+ local resolved = package.loaders[-2](name)
end
-package.loaders[2] = function(name) -- was [#package.loaders+1]
- if file.suffix(name) == "" then
- name = file.addsuffix(name,"lua") -- maybe a list
- if trace_libraries then -- mode detail
- report_libraries("! locating '%s' with forced suffix",name)
- end
- else
- if trace_libraries then -- mode detail
- report_libraries("! locating '%s'",name)
- end
+local function loadedbyformat(name,rawname,suffixes,islib)
+ if trace_libraries then
+ report_libraries("! locating %q as %q using formats %q",rawname,name,concat(suffixes))
end
- for i=1,#libformats do
- local format = libformats[i]
+ for i=1,#suffixes do -- so we use findfile and not a lookup loop
+ local format = suffixes[i]
local resolved = resolvers.findfile(name,format) or ""
- if trace_libraries then -- mode detail
- report_libraries("! checking for '%s' using 'libformat path': '%s'",name,format)
+ if trace_libraries then
+ report_libraries("! checking for %q' using format %q",name,format)
end
if resolved ~= "" then
if trace_libraries then
- report_libraries("! lib '%s' located via environment: '%s'",name,resolved)
- end
- return loadfile(resolved)
- end
- end
- -- libpaths
- local libpaths, clibpaths = package.libpaths(), package.clibpaths()
- local simple = gsub(name,"%.lua$","")
- local simple = gsub(simple,"%.","/")
- local resolved = loaded(p_libpaths,name,simple) or loaded(libpaths,name,simple) or loaded(a_libpaths,name,simple)
- if resolved then
- return resolved
- end
- --
- local libname = file.addsuffix(simple,os.libsuffix)
- for i=1,#clibformats do
- -- better have a dedicated loop
- local format = clibformats[i]
- local paths = resolvers.expandedpathlistfromvariable(format)
- for p=1,#paths do
- local path = paths[p]
- local resolved = file.join(path,libname)
- if trace_libraries then -- mode detail
- report_libraries("! checking for '%s' using 'clibformat path': '%s'",libname,path)
+ report_libraries("! lib %q located on %q",name,resolved)
end
- if file.is_readable(resolved) then
- if trace_libraries then
- report_libraries("! lib '%s' located via 'clibformat': '%s'",libname,resolved)
- end
- return package.loadlib(resolved,name)
+ if islib then
+ return loadedaslib(resolved,rawname)
+ else
+ return loadfile(resolved)
end
end
end
- for i=1,#clibpaths do -- package.path, might become option
- local libpath = clibpaths[i]
- local resolved = gsub(libpath,"?",simple)
- if trace_libraries then -- more detail
- report_libraries("! checking for '%s' on 'package.cpath': '%s'",simple,libpath)
+end
+
+local function loadedbypath(name,rawname,paths,islib,what)
+ if trace_libraries then
+ report_libraries("! locating %q as %q on %q paths",rawname,name,what)
+ end
+ for p=1,#paths do
+ local path = paths[p]
+ local resolved = file.join(path,name)
+ if trace_libraries then -- mode detail
+ report_libraries("! checking for %q using %q path %q",name,what,path)
end
- if file.is_readable(resolved) then
+ if is_readable(resolved) then
if trace_libraries then
- report_libraries("! lib '%s' located via 'package.cpath': '%s'",name,resolved)
+ report_libraries("! lib %q located on %q",name,resolved)
+ end
+ if islib then
+ return loadedaslib(resolved,rawname)
+ else
+ return loadfile(resolved)
end
- return package.loadlib(resolved,name)
- end
- end
- -- just in case the distribution is messed up
- if trace_loading then -- more detail
- report_libraries("! checking for '%s' using 'luatexlibs': '%s'",name)
- end
- local resolved = resolvers.findfile(file.basename(name),'luatexlibs') or ""
- if resolved ~= "" then
- if trace_libraries then
- report_libraries("! lib '%s' located by basename via environment: '%s'",name,resolved)
end
- return loadfile(resolved)
end
+end
+
+local function notloaded(name)
if trace_libraries then
- report_libraries('? unable to locate lib: %s',name)
+ report_libraries("? unable to locate library %q",name)
end
--- return "unable to locate " .. name
end
-resolvers.loadlualib = require
-
--- -- -- --
-
-package.obsolete = package.obsolete or { }
-
-package.append_libpath = appendtolibpath -- will become obsolete
-package.prepend_libpath = prependtolibpath -- will become obsolete
+package.loaders[2] = function(name)
+ local thename = gsub(name,"%.","/")
+ local luaname = file.addsuffix(thename,"lua")
+ local libname = file.addsuffix(thename,os.libsuffix)
+ return
+ loadedbyformat(luaname,name,libsuffixes, false)
+ or loadedbyformat(libname,name,clibsuffixes, true)
+ or loadedbypath (luaname,name,getlibpaths (),false,"lua")
+ or loadedbypath (luaname,name,getclibpaths(),false,"lua")
+ or loadedbypath (libname,name,getclibpaths(),true, "lib")
+ or loadedbylua (name)
+ or notloaded (name)
+end
-package.obsolete.append_libpath = appendtolibpath -- will become obsolete
-package.obsolete.prepend_libpath = prependtolibpath -- will become obsolete
+-- package.loaders[3] = nil
+-- package.loaders[4] = nil
+resolvers.loadlualib = require
diff --git a/tex/context/base/data-pre.lua b/tex/context/base/data-pre.lua
index 4e82b6186..214477d87 100644
--- a/tex/context/base/data-pre.lua
+++ b/tex/context/base/data-pre.lua
@@ -121,6 +121,8 @@ function resolvers.resetresolve(str)
resolved, abstract = { }, { }
end
+-- todo: use an lpeg (see data-lua for !! / stripper)
+
local function resolve(str) -- use schemes, this one is then for the commandline only
if type(str) == "table" then
local t = { }
diff --git a/tex/context/base/data-res.lua b/tex/context/base/data-res.lua
index e51b980ff..9236cbe02 100644
--- a/tex/context/base/data-res.lua
+++ b/tex/context/base/data-res.lua
@@ -805,7 +805,7 @@ function resolvers.expandedpathlist(str)
end
end
-function resolvers.expandedpathlistfromvariable(str) -- brrr
+function resolvers.expandedpathlistfromvariable(str) -- brrr / could also have cleaner ^!! /$ //
str = lpegmatch(dollarstripper,str)
local tmp = resolvers.variableofformatorsuffix(str)
return resolvers.expandedpathlist(tmp ~= "" and tmp or str)
diff --git a/tex/context/base/l-os.lua b/tex/context/base/l-os.lua
index 700affdac..799f44957 100644
--- a/tex/context/base/l-os.lua
+++ b/tex/context/base/l-os.lua
@@ -22,18 +22,28 @@ if not modules then modules = { } end modules ['l-os'] = {
-- os.name : windows | msdos | linux | macosx | solaris | .. | generic (new)
-- os.platform : extended os.name with architecture
+-- os.sleep() => socket.sleep()
+-- math.randomseed(tonumber(string.sub(string.reverse(tostring(math.floor(socket.gettime()*10000))),1,6)))
+
-- maybe build io.flush in os.execute
local os = os
-local date = os.date
+local date, time = os.date, os.time
local find, format, gsub, upper, gmatch = string.find, string.format, string.gsub, string.upper, string.gmatch
local concat = table.concat
local random, ceil, randomseed = math.random, math.ceil, math.randomseed
-local rawget, rawset, type, getmetatable, setmetatable, tonumber = rawget, rawset, type, getmetatable, setmetatable, tonumber
+local rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring = rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring
-- The following code permits traversing the environment table, at least
-- in luatex. Internally all environment names are uppercase.
+-- The randomseed in Lua is not that random, although this depends on the operating system as well
+-- as the binary (Luatex is normally okay). But to be sure we set the seed anyway.
+
+math.initialseed = tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6))
+
+randomseed(math.initialseed)
+
if not os.__getenv__ then
os.__getenv__ = os.getenv
@@ -375,10 +385,41 @@ end
local timeformat = format("%%s%s",os.timezone(true))
local dateformat = "!%Y-%m-%d %H:%M:%S"
-function os.fulltime(t)
+function os.fulltime(t,default)
+ t = tonumber(t) or 0
+ if t > 0 then
+ -- valid time
+ elseif default then
+ return default
+ else
+ t = nil
+ end
return format(timeformat,date(dateformat,t))
end
+local dateformat = "%Y-%m-%d %H:%M:%S"
+
+function os.localtime(t,default)
+ t = tonumber(t) or 0
+ if t > 0 then
+ -- valid time
+ elseif default then
+ return default
+ else
+ t = nil
+ end
+ return date(dateformat,t)
+end
+
+function os.converttime(t,default)
+ local t = tonumber(t)
+ if t and t > 0 then
+ return date(dateformat,t)
+ else
+ return default or "-"
+ end
+end
+
local memory = { }
local function which(filename)
diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua
index 413d31eb5..2b3319e45 100644
--- a/tex/context/base/l-table.lua
+++ b/tex/context/base/l-table.lua
@@ -913,23 +913,27 @@ function table.reversed(t)
end
end
-function table.sequenced(t,sep,simple) -- hash only
- local s, n = { }, 0
- for k, v in sortedhash(t) do
- if simple then
- if v == true then
- n = n + 1
- s[n] = k
- elseif v and v~= "" then
+function table.sequenced(t,sep) -- hash only
+ if t then
+ local s, n = { }, 0
+ for k, v in sortedhash(t) do
+ if simple then
+ if v == true then
+ n = n + 1
+ s[n] = k
+ elseif v and v~= "" then
+ n = n + 1
+ s[n] = k .. "=" .. tostring(v)
+ end
+ else
n = n + 1
s[n] = k .. "=" .. tostring(v)
end
- else
- n = n + 1
- s[n] = k .. "=" .. tostring(v)
end
+ return concat(s, sep or " | ")
+ else
+ return ""
end
- return concat(s, sep or " | ")
end
function table.print(t,...)
diff --git a/tex/context/base/luat-env.lua b/tex/context/base/luat-env.lua
index 4d9a44d42..c8a391e76 100644
--- a/tex/context/base/luat-env.lua
+++ b/tex/context/base/luat-env.lua
@@ -261,7 +261,7 @@ function environment.texfile(filename)
return resolvers.findfile(filename,'tex')
end
-function environment.luafile(filename)
+function environment.luafile(filename) -- needs checking
local resolved = resolvers.findfile(filename,'tex') or ""
if resolved ~= "" then
return resolved
diff --git a/tex/context/base/m-pstricks.lua b/tex/context/base/m-pstricks.lua
index 7f795feac..b151e313a 100644
--- a/tex/context/base/m-pstricks.lua
+++ b/tex/context/base/m-pstricks.lua
@@ -39,12 +39,12 @@ local template = [[
\stoptext
]]
-local modules = { }
+local loaded = { }
local graphics = 0
function moduledata.pstricks.usemodule(names)
for name in gmatch(names,"([^%s,]+)") do
- modules[#modules+1] = format([[\readfile{%s}{}{}]],name)
+ loaded[#loaded+1] = format([[\readfile{%s}{}{}]],name)
end
end
@@ -55,10 +55,10 @@ function moduledata.pstricks.process(n)
local tmpfile = name .. ".tmp"
local epsfile = name .. ".ps"
local pdffile = name .. ".pdf"
- local modules = concat(modules,"\n")
+ local loaded = concat(loaded,"\n")
os.remove(epsfile)
os.remove(pdffile)
- io.savedata(tmpfile,format(template,modules,data))
+ io.savedata(tmpfile,format(template,loaded,data))
os.execute(format("mtxrun --script texexec %s --once --dvips",tmpfile))
if lfs.isfile(epsfile) then
os.execute(format("ps2pdf %s %s",epsfile,pdffile))
diff --git a/tex/context/base/mult-low.lua b/tex/context/base/mult-low.lua
index 7d5078b60..bffdd288f 100644
--- a/tex/context/base/mult-low.lua
+++ b/tex/context/base/mult-low.lua
@@ -271,6 +271,8 @@ return {
--
"startnointerference", "stopnointerference",
--
+ "twodigits","threedigits",
+ --
"strut", "setstrut", "strutbox", "strutht", "strutdp", "strutwd", "struthtdp", "begstrut", "endstrut", "lineheight",
}
}
diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf
index c31ba403b..e5263bffa 100644
--- a/tex/context/base/status-files.pdf
+++ b/tex/context/base/status-files.pdf
Binary files differ
diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf
index 8ad2ea57b..6a8332f63 100644
--- a/tex/context/base/status-lua.pdf
+++ b/tex/context/base/status-lua.pdf
Binary files differ
diff --git a/tex/context/base/util-sql-loggers.lua b/tex/context/base/util-sql-loggers.lua
new file mode 100644
index 000000000..b2dccf4b5
--- /dev/null
+++ b/tex/context/base/util-sql-loggers.lua
@@ -0,0 +1,277 @@
+if not modules then modules = { } end modules ['util-sql-loggers'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code and currently part of the base installation simply
+-- because it's easier to dirtribute this way. Eventually it will be documented
+-- and the related scripts will show up as well.
+
+local tonumber = tonumber
+local format = string.format
+local concat = table.concat
+local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
+local random = math.random
+
+local sql = utilities.sql
+local loggers = { }
+sql.loggers = loggers
+
+local trace_sql = false trackers.register("sql.loggers.trace", function(v) trace_sql = v end)
+local report = logs.reporter("sql","loggers")
+
+loggers.newtoken = sql.tokens.new
+local makeconverter = sql.makeconverter
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "loggers")
+end
+
+loggers.usedb = checkeddb
+
+local totype = {
+ ["error"] = 1, [1] = 1, ["1"] = 1,
+ ["warning"] = 2, [2] = 2, ["2"] = 2,
+ ["debug"] = 3, [3] = 3, ["3"] = 3,
+ ["info"] = 4, [4] = 4, ["4"] = 4,
+}
+
+local fromtype = {
+ ["error"] = "error", [1] = "error", ["1"] = "error",
+ ["warning"] = "warning", [2] = "warning", ["2"] = "warning",
+ ["debug"] = "debug", [3] = "debug", ["3"] = "debug",
+ ["info"] = "info", [4] = "info", ["4"] = "info",
+}
+
+table.setmetatableindex(totype, function() return 4 end)
+table.setmetatableindex(fromtype,function() return "info" end)
+
+loggers.totype = totype
+loggers.fromtype = fromtype
+
+local template =[[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `time` int(11) NOT NULL,
+ `type` int(11) NOT NULL,
+ `action` varchar(15) NOT NULL,
+ `data` longtext,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `id_unique_key` (`id`)
+ )
+ DEFAULT CHARSET = utf8 ;
+]]
+
+function loggers.createdb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %q created in %q",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ DROP TABLE IF EXISTS %basename% ;
+]]
+
+function loggers.deletedb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %q removed in %q",db.name,db.base)
+
+end
+
+local template =[[
+ INSERT INTO %basename% (
+ `time`,
+ `type`,
+ `action`,
+ `data`
+ ) VALUES (
+ %time%,
+ %type%,
+ '%action%',
+ '%[data]%'
+ ) ;
+]]
+
+function loggers.save(db,data) -- beware, we pass type and action in the data (saves a table)
+
+ if data then
+
+ local time = ostime()
+ local kind = totype[data.type]
+ local action = data.action or "unknown"
+
+ data.type = nil
+ data.action = nil
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ time = ostime(),
+ type = kind,
+ action = action,
+ data = data and db.serialize(data,"return") or "",
+ },
+ }
+
+ end
+
+end
+
+-- local template =[[
+-- REMOVE FROM
+-- %basename%
+-- WHERE
+-- `token` = '%token%' ;
+-- ]]
+--
+-- function loggers.remove(db,token)
+--
+-- db.execute {
+-- template = template,
+-- variables = {
+-- basename = db.basename,
+-- token = token,
+-- },
+-- }
+--
+-- if trace_sql then
+-- report("removed: %s",token)
+-- end
+--
+-- end
+
+local template_nop =[[
+ SELECT
+ `time`,
+ `type`,
+ `action`,
+ `data`
+ FROM
+ %basename%
+ ORDER BY
+ `time`, `type`, `action`
+ DESC LIMIT
+ %limit% ;
+]]
+
+local template_yes =[[
+ SELECT
+ `time`,
+ `type`,
+ `action`,
+ `data`
+ FROM
+ %basename%
+ %WHERE%
+ ORDER BY
+ `time`, `type`, `action`
+ DESC LIMIT
+ %limit% ;
+]]
+
+local converter = makeconverter {
+ -- { name = "time", type = os.localtime },
+ { name = "time", type = "number" },
+ { name = "type", type = fromtype },
+ { name = "action", type = "string" },
+ { name = "data", type = "deserialize" },
+}
+
+function loggers.collect(db,specification)
+
+ specification = specification or { }
+
+ local start = specification.start
+ local stop = specification.stop
+ local limit = specification.limit or 100
+ local kind = specification.type
+ local action = specification.action
+
+ local filtered = start or stop
+
+ local where = { }
+
+ if filtered then
+ local today = os.date("*t")
+
+ if type(start) ~= "table" then
+ start = { }
+ end
+ start = os.time {
+ day = start.day or today.day,
+ month = start.month or today.month,
+ year = start.year or today.year,
+ hour = start.hour or 0,
+ minute = start.minute or 0,
+ second = start.second or 0,
+ isdst = true,
+ }
+
+ if type(stop) ~= "table" then
+ stop = { }
+ end
+ stop = os.time {
+ day = stop.day or today.day,
+ month = stop.month or today.month,
+ year = stop.year or today.year,
+ hour = stop.hour or 24,
+ minute = stop.minute or 0,
+ second = stop.second or 0,
+ isdst = true,
+ }
+
+ -- report("filter: %s => %s",start,stop)
+
+ where[#where+1] = format("`time` BETWEEN %s AND %s",start,stop)
+
+ end
+
+ if kind then
+ where[#where+1] = format("`type` = %s",totype[kind])
+ end
+
+ if action then
+ where[#where+1] = format("`action` = '%s'",action)
+ end
+
+ local records = db.execute {
+ template = filtered and template_yes or template_nop,
+ converter = converter,
+ variables = {
+ basename = db.basename,
+ limit = limit,
+ WHERE = #where > 0 and format("WHERE\n%s",concat(where," AND ")) or "",
+ },
+ }
+
+ if trace_sql then
+ report("collected: %s loggers",#records)
+ end
+
+ return records, keys
+
+end
diff --git a/tex/context/base/util-sql-sessions.lua b/tex/context/base/util-sql-sessions.lua
new file mode 100644
index 000000000..40556dd5e
--- /dev/null
+++ b/tex/context/base/util-sql-sessions.lua
@@ -0,0 +1,349 @@
+if not modules then modules = { } end modules ['util-sql-sessions'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code and currently part of the base installation simply
+-- because it's easier to dirtribute this way. Eventually it will be documented
+-- and the related scripts will show up as well.
+
+-- maybe store threshold in session (in seconds)
+
+local tonumber = tonumber
+local format = string.format
+local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
+local random = math.random
+
+-- In older frameworks we kept a session table in memory. This time we
+-- follow a route where we store session data in a sql table. Each session
+-- has a token (similar to what we do on q2p and pod services), a data
+-- blob which is just a serialized lua table (we could consider a dump instead)
+-- and two times: the creation and last accessed time. The first one is handy
+-- for statistics and the second one for cleanup. Both are just numbers so that
+-- we don't have to waste code on conversions. Anyhow, we provide variants so that
+-- we can always choose what is best.
+
+local sql = utilities.sql
+local sessions = { }
+sql.sessions = sessions
+
+local trace_sql = false trackers.register("sql.sessions.trace", function(v) trace_sql = v end)
+local report = logs.reporter("sql","sessions")
+
+sessions.newtoken = sql.tokens.new
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "sessions")
+end
+
+sessions.usedb = checkeddb
+
+local template =[[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `token` varchar(50) NOT NULL,
+ `data` longtext NOT NULL,
+ `created` int(11) NOT NULL,
+ `accessed` int(11) NOT NULL,
+ UNIQUE KEY `token_unique_key` (`token`)
+ )
+ DEFAULT CHARSET = utf8 ;
+]]
+
+function sessions.createdb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %q created in %q",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ DROP TABLE IF EXISTS %basename% ;
+]]
+
+function sessions.deletedb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %q removed in %q",db.name,db.base)
+
+end
+
+local template =[[
+ INSERT INTO %basename% (
+ `token`,
+ `created`,
+ `accessed`,
+ `data`
+ ) VALUES (
+ '%token%',
+ %time%,
+ %time%,
+ '%[data]%'
+ ) ;
+]]
+
+function sessions.create(db,data)
+
+ local token = sessions.newtoken()
+ local time = ostime()
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = time,
+ data = db.serialize(data or { },"return")
+ },
+ }
+
+ if trace_sql then
+ report("created: %s at %s",token,osfulltime(time))
+ end
+
+ return {
+ token = token,
+ created = time,
+ accessed = time,
+ data = data,
+ }
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `data` = '%[data]%',
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.save(db,session)
+
+ local time = ostime()
+ local data = db.serialize(session.data or { },"return")
+ local token = session.token
+
+ session.accessed = time
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = ostime(),
+ data = data,
+ },
+ }
+
+ if trace_sql then
+ report("saved: %s at %s",token,osfulltime(time))
+ end
+
+ return session
+end
+
+local template = [[
+ UPDATE
+ %basename%
+ SET
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.touch(db,token)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = ostime(),
+ },
+ }
+
+end
+
+local template = [[
+ UPDATE
+ %basename%
+ SET
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.restore(db,token)
+
+ local records, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = ostime(),
+ },
+ }
+
+ local record = records and records[1]
+
+ if record then
+ if trace_sql then
+ report("restored: %s",token)
+ end
+ record.data = db.deserialize(record.data or "")
+ return record, keys
+ elseif trace_sql then
+ report("unknown: %s",token)
+ end
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.remove(db,token)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ },
+ }
+
+ if trace_sql then
+ report("removed: %s",token)
+ end
+
+end
+
+local template_collect_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ ORDER BY
+ `created` ;
+]]
+
+local template_collect_nop =[[
+ SELECT
+ `accessed`,
+ `created`,
+ `accessed`,
+ `token`
+ FROM
+ %basename%
+ ORDER BY
+ `created` ;
+]]
+
+function sessions.collect(db,nodata)
+
+ local records, keys = db.execute {
+ template = nodata and template_collect_nop or template_collect_yes,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("collected: %s sessions",#records)
+ end
+
+ return records, keys
+
+end
+
+local template_cleanup_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `created` ;
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% ;
+]]
+
+local template_cleanup_nop =[[
+ SELECT
+ `accessed`,
+ `created`,
+ `accessed`,
+ `token`
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `created` ;
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% ;
+]]
+
+function sessions.cleanupdb(db,delta,nodata)
+
+ local time = ostime()
+
+ local records, keys = db.execute {
+ template = nodata and template_cleanup_nop or template_cleanup_yes,
+ variables = {
+ basename = db.basename,
+ time = time - delta
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("cleaned: %s seconds before %s",delta,osfulltime(time))
+ end
+
+ return records, keys
+
+end
diff --git a/tex/context/base/util-sql-tickets.lua b/tex/context/base/util-sql-tickets.lua
new file mode 100644
index 000000000..ad885e34e
--- /dev/null
+++ b/tex/context/base/util-sql-tickets.lua
@@ -0,0 +1,698 @@
+if not modules then modules = { } end modules ['util-sql-tickets'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code and currently part of the base installation simply
+-- because it's easier to dirtribute this way. Eventually it will be documented
+-- and the related scripts will show up as well.
+
+local tonumber = tonumber
+local format = string.format
+local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
+local random = math.random
+local concat = table.concat
+
+local sql = utilities.sql
+local tickets = { }
+sql.tickets = tickets
+
+local trace_sql = false trackers.register("sql.tickets.trace", function(v) trace_sql = v end)
+local report = logs.reporter("sql","tickets")
+
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+local execute = sql.execute
+
+tickets.newtoken = sql.tokens.new
+
+local statustags = { [0] = -- beware index can be string or number, maybe status should be a string in the database
+ "unknown",
+ "pending",
+ "busy",
+ "finished",
+ "error",
+ "deleted",
+}
+
+local status = table.swapped(statustags)
+
+tickets.status = status
+tickets.statustags = statustags
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "tickets")
+end
+
+tickets.usedb = checkeddb
+
+local template =[[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `token` varchar(50) NOT NULL,
+ `subtoken` INT(11) NOT NULL,
+ `created` int(11) NOT NULL,
+ `accessed` int(11) NOT NULL,
+ `category` int(11) NOT NULL,
+ `status` int(11) NOT NULL,
+ `usertoken` varchar(50) NOT NULL,
+ `data` longtext NOT NULL,
+ `comment` longtext NOT NULL,
+
+ PRIMARY KEY (`id`),
+ UNIQUE INDEX `id_unique_index` (`id` ASC),
+ KEY `token_unique_key` (`token`)
+ )
+ DEFAULT CHARSET = utf8 ;
+]]
+
+function tickets.createdb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ local data, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %q created in %q",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ DROP TABLE IF EXISTS %basename% ;
+]]
+
+function tickets.deletedb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ local data, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %q removed in %q",db.name,db.base)
+
+end
+
+local template =[[
+ LOCK TABLES
+ %basename%
+ WRITE ;
+ INSERT INTO %basename% (
+ `token`,
+ `subtoken`,
+ `created`,
+ `accessed`,
+ `status`,
+ `category`,
+ `usertoken`,
+ `data`,
+ `comment`
+ ) VALUES (
+ '%token%',
+ %subtoken%,
+ %time%,
+ %time%,
+ %status%,
+ %category%,
+ '%usertoken%',
+ '%[data]%',
+ '%[comment]%'
+ ) ;
+ SELECT
+ LAST_INSERT_ID() AS `id` ;
+ UNLOCK TABLES ;
+]]
+
+function tickets.create(db,ticket)
+
+ local token = ticket.token or tickets.newtoken()
+ local time = ostime()
+ local status = ticket.status or 0
+ local category = ticket.category or 0
+ local subtoken = ticket.subtoken or 0
+ local usertoken = ticket.usertoken or ""
+ local comment = ticket.comment or ""
+
+ local result, message = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ subtoken = subtoken,
+ time = time,
+ status = status,
+ category = category,
+ usertoken = usertoken,
+ data = db.serialize(ticket.data or { },"return"),
+ comment = comment,
+ },
+ }
+
+ if trace_sql then
+ report("created: %s at %s",token,osfulltime(time))
+ end
+
+ local r = result and result[1]
+
+ if r then
+
+ return {
+ id = r.id,
+ token = token,
+ subtoken = subtoken,
+ created = time,
+ accessed = time,
+ status = status,
+ category = category,
+ usertoken = usertoken,
+ data = data,
+ comment = comment,
+ }
+
+ end
+end
+
+local template =[[
+ LOCK TABLES
+ %basename%
+ WRITE ;
+ UPDATE %basename% SET
+ `data` = '%[data]%',
+ `status` = %status%,
+ `accessed` = %time%
+ WHERE
+ `id` = %id% ;
+ UNLOCK TABLES ;
+]]
+
+function tickets.save(db,ticket)
+
+ local time = ostime()
+ local data = db.serialize(ticket.data or { },"return")
+ local status = ticket.status or 0
+ local id = ticket.id
+
+ if not status then
+ status = 0
+ ticket.status = 0
+ end
+
+ ticket.accessed = time
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ time = ostime(),
+ status = status,
+ data = data,
+ },
+ }
+
+ if trace_sql then
+ report("saved: id %s, time %s",id,osfulltime(time))
+ end
+
+ return ticket
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `id` = %id% ;
+]]
+
+function tickets.restore(db,id)
+
+ local record, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ time = ostime(),
+ },
+ }
+
+ local record = record and record[1]
+
+ if record then
+ if trace_sql then
+ report("restored: id %s",id)
+ end
+ record.data = db.deserialize(record.data or "")
+ return record
+ elseif trace_sql then
+ report("unknown: id %s",id)
+ end
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `id` = %id% ;
+]]
+
+function tickets.remove(db,id)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ },
+ }
+
+ if trace_sql then
+ report("removed: id %s",id)
+ end
+
+end
+
+local template_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ ORDER BY
+ `created` ;
+]]
+
+local template_nop =[[
+ SELECT
+ `created`,
+ `usertoken`,
+ `accessed`,
+ `status`
+ FROM
+ %basename%
+ ORDER BY
+ `created` ;
+]]
+
+function tickets.collect(db,nodata)
+
+ local records, keys = db.execute {
+ template = nodata and template_nop or template_yes,
+ variables = {
+ basename = db.basename,
+ token = token,
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("collected: %s tickets",#records)
+ end
+
+ return records, keys
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% OR `status` = 5 ;
+]]
+
+local template_cleanup_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `created` ;
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% OR `status` = 5 ;
+]]
+
+local template_cleanup_nop =[[
+ SELECT
+ `accessed`,
+ `created`,
+ `accessed`,
+ `token`
+ `usertoken`
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `created` ;
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% OR `status` = 5 ;
+]]
+
+function tickets.cleanupdb(db,delta,nodata) -- maybe delta in db
+
+ local time = delta and (ostime() - delta) or 0
+
+ local records, keys = db.execute {
+ template = nodata and template_cleanup_nop or template_cleanup_yes,
+ variables = {
+ basename = db.basename,
+ time = time,
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("cleaned: %s seconds before %s",delta,osfulltime(time))
+ end
+
+ return records, keys
+
+end
+
+-- status related functions
+
+local template =[[
+ SELECT
+ `status`
+ FROM
+ %basename%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function tickets.getstatus(db,token)
+
+ local record, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ },
+ }
+
+ local record = record and record[1]
+
+ return record and record.status or 0
+
+end
+
+local template =[[
+ SELECT
+ `status`
+ FROM
+ %basename%
+ WHERE
+ `status` = 5 OR `accessed` < %time% ;
+]]
+
+function tickets.getobsolete(db,delta)
+
+ local time = delta and (ostime() - delta) or 0
+
+ local records = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ time = time,
+ },
+ }
+
+ db.unpackdata(records)
+
+ return records
+
+end
+
+local template =[[
+ SELECT
+ `id`
+ FROM
+ %basename%
+ WHERE
+ `status` = %status%
+ LIMIT
+ 1 ;
+]]
+
+function tickets.hasstatus(db,status)
+
+ local record = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ status = status or 0,
+ },
+ }
+
+ return record and #record > 0 or false
+
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `status` = %status%,
+ `accessed` = %time%
+ WHERE
+ `id` = %id% ;
+]]
+
+function tickets.setstatus(db,id,status)
+
+ local record, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ time = ostime(),
+ status = status or 0,
+ },
+ }
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `status` IN (%status%) ;
+]]
+
+function tickets.prunedb(db,status)
+
+ if type(status) == "table" then
+ status = concat(status,",")
+ end
+
+ local data, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ status = status or 0,
+ },
+ }
+
+ if trace_sql then
+ report("pruned: status %s removed",status)
+ end
+
+end
+
+local template_a = [[
+ LOCK TABLES
+ %basename%
+ WRITE ;
+ SET
+ @first_token = "?" ;
+ SELECT
+ `token`
+ INTO
+ @first_token
+ FROM
+ %basename%
+ WHERE
+ `status` = %status%
+ ORDER BY
+ `id`
+ LIMIT 1 ;
+ UPDATE
+ %basename%
+ SET
+ `status` = %newstatus%,
+ `accessed` = %time%
+ WHERE
+ `token` = @first_token ;
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `token` = @first_token
+ ORDER BY
+ `id` ;
+ UNLOCK TABLES ;
+]]
+
+local template_b = [[
+ SET
+ @first_token = "?" ;
+ SELECT
+ `token`
+ INTO
+ @first_token
+ FROM
+ %basename%
+ WHERE
+ `status` = %status%
+ ORDER BY
+ `id`
+ LIMIT 1 ;
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `token` = @first_token
+ ORDER BY
+ `id` ;
+]]
+
+function tickets.getfirstwithstatus(db,status,newstatus)
+
+ local records
+
+ if type(newstatus) == "number" then
+
+ records = db.execute {
+ template = template_a,
+ variables = {
+ basename = db.basename,
+ status = status or 0,
+ newstatus = newstatus,
+ time = ostime(),
+ },
+ }
+
+
+ else
+
+ records = db.execute {
+ template = template_b,
+ variables = {
+ basename = db.basename,
+ status = status or 0,
+ },
+ }
+
+ end
+
+ if type(records) == "table" and #records > 0 then
+
+ for i=1,#records do
+ local record = records[i]
+ record.data = db.deserialize(record.data or "")
+ record.status = newstatus
+ end
+
+ return records
+
+ end
+end
+
+local template =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `usertoken` = '%usertoken%' AND `status` != 5
+ ORDER BY
+ `created` ;
+]]
+
+function tickets.getusertickets(db,usertoken)
+
+ -- todo: update accessed
+ -- todo: get less fields
+ -- maybe only data for status changed (hard to check)
+
+ local records, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ usertoken = usertoken,
+ },
+ }
+
+ db.unpackdata(records)
+
+ return records
+
+end
+
+local template =[[
+ LOCK TABLES
+ %basename%
+ WRITE ;
+ UPDATE %basename% SET
+ `status` = 5
+ WHERE
+ `usertoken` = '%usertoken%' ;
+ UNLOCK TABLES ;
+]]
+
+function tickets.removeusertickets(db,usertoken)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ usertoken = usertoken,
+ },
+ }
+
+ if trace_sql then
+ report("removed: usertoken %s",usertoken)
+ end
+
+end
+
+-- -- left-overs --
+
+-- LOCK TABLES `m4alltickets` WRITE ;
+-- CREATE TEMPORARY TABLE ticketset SELECT * FROM m4alltickets WHERE token = @first_token ;
+-- DROP TABLE ticketset ;
+-- UNLOCK TABLES ;
diff --git a/tex/context/base/util-sql.lua b/tex/context/base/util-sql.lua
index f86607377..fea60c96a 100644
--- a/tex/context/base/util-sql.lua
+++ b/tex/context/base/util-sql.lua
@@ -25,6 +25,9 @@ if not modules then modules = { } end modules ['util-sql'] = {
-- efficiency issues (like creating a keys and types table for each row) but that could be
-- optimized. Anyhow, fecthing results can be done as follows:
+-- We use the template mechanism from util-tpl which inturn is just using the dos cq
+-- windows convention of %whatever% variables that I've used for ages.
+
-- local function collect_1(r)
-- local t = { }
-- for i=1,r:numrows() do
@@ -75,7 +78,7 @@ if not modules then modules = { } end modules ['util-sql'] = {
local format, match = string.format, string.match
local random = math.random
-local rawset, setmetatable, loadstring, type = rawset, setmetatable, loadstring, type
+local rawset, setmetatable, getmetatable, loadstring, type = rawset, setmetatable, getmetatable, loadstring, type
local P, S, V, C, Cs, Ct, Cc, Cg, Cf, patterns, lpegmatch = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.patterns, lpeg.match
local concat = table.concat
@@ -87,6 +90,9 @@ local trace_sql = false trackers.register("sql.trace", function(v) trace
local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
local report_state = logs.reporter("sql")
+-- trace_sql = true
+-- trace_queries = true
+
utilities.sql = utilities.sql or { }
local sql = utilities.sql
@@ -96,8 +102,11 @@ local loadtemplate = utilities.templates.load
local methods = { }
sql.methods = methods
-sql.serialize = table.fastserialize
-sql.deserialize = table.deserialize
+local serialize = table.fastserialize
+local deserialize = table.deserialize
+
+sql.serialize = serialize
+sql.deserialize = deserialize
local defaults = { __index =
{
@@ -207,14 +216,19 @@ local function validspecification(specification)
presets = dofile(presets)
end
if type(presets) == "table" then
- setmetatable(presets,defaults)
+ local m = getmetatable(presets)
+ if m then
+ setmetatable(m,defaults)
+ else
+ setmetatable(presets,defaults)
+ end
setmetatable(specification,{ __index = presets })
else
setmetatable(specification,defaults)
end
- local templatefile = specification.templatefile
- local queryfile = specification.queryfile or file.nameonly(templatefile) .. "-temp.sql"
- local resultfile = specification.resultfile or file.nameonly(templatefile) .. "-temp.dat"
+ local templatefile = specification.templatefile or "query"
+ local queryfile = specification.queryfile or presets.queryfile or file.nameonly(templatefile) .. "-temp.sql"
+ local resultfile = specification.resultfile or presets.resultfile or file.nameonly(templatefile) .. "-temp.dat"
specification.queryfile = queryfile
specification.resultfile = resultfile
if trace_sql then
@@ -310,7 +324,7 @@ sql.splitdata = splitdata
local function execute(specification)
if trace_sql then
- report_state("executing")
+ report_state("executing client")
end
if not validspecification(specification) then
report_state("error in specification")
@@ -342,6 +356,7 @@ methods.client = {
execute = execute,
serialize = serialize,
deserialize = deserialize,
+ usesfiles = true,
}
local function dataloaded(specification)
@@ -368,7 +383,7 @@ end
local function execute(specification)
if trace_sql then
- report_state("executing")
+ report_state("executing lmxsql")
end
if not validspecification(specification) then
report_state("error in specification")
@@ -400,6 +415,7 @@ methods.lmxsql = {
execute = execute,
serialize = serialize,
deserialize = deserialize,
+ usesfiles = true,
}
local mysql = nil
@@ -447,7 +463,7 @@ local query = whitespace
* whitespace
local splitter = Ct(query * (separator * query)^0)
-local function datafetched(specification,query)
+local function datafetched(specification,query,converter)
local id = specification.id
local session, connection
if id then
@@ -466,18 +482,32 @@ local function datafetched(specification,query)
connection = connect(session,specification)
end
if not connection then
+ report_state("error in connection: %s@%s to %s:%s",
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
return { }, { }
end
query = lpegmatch(splitter,query)
- local result, message
+ local result, message, okay
for i=1,#query do
local q = query[i]
- result, message = connection:execute(q)
- if message then
- report_state("error in query: %s",string.collapsespaces(q))
+ local r, m = connection:execute(q)
+ if m then
+ report_state("error in query, stage 1: %s",string.collapsespaces(q))
+ message = message and format("%s\n%s",message,m) or m
+ end
+ local t = type(r)
+ if t == "userdata" then
+ result = r
+ okay = true
+ elseif t == "number" then
+ okay = true
end
end
- if not result and id then
+ if not okay and id then
if session then
session:close()
end
@@ -489,34 +519,46 @@ local function datafetched(specification,query)
cache[id] = { session = session, connection = connection }
for i=1,#query do
local q = query[i]
- result, message = connection:execute(q)
- if message then
- report_state("error in query: %s",string.collapsespaces(q))
+ local r, m = connection:execute(q)
+ if m then
+ report_state("error in query, stage 2: %s",string.collapsespaces(q))
+ message = message and format("%s\n%s",message,m) or m
+ end
+ local t = type(r)
+ if t == "userdata" then
+ result = r
+ okay = true
+ elseif t == "number" then
+ okay = true
end
end
end
local data, keys
- if result and type(result) ~= "number" then
- keys = result:getcolnames()
- if keys then
- local n = result:numrows() or 0
- if n == 0 then
- data = { }
- -- elseif n == 1 then
- -- -- data = { result:fetch({},"a") }
- else
- data = { }
- -- for i=1,n do
- -- data[i] = result:fetch({},"a")
- -- end
- local k = #keys
- for i=1,n do
- local v = { result:fetch() }
- local d = { }
- for i=1,k do
- d[keys[i]] = v[i]
+ if result then
+ if converter then
+ data = converter(result,deserialize)
+ else
+ keys = result:getcolnames()
+ if keys then
+ local n = result:numrows() or 0
+ if n == 0 then
+ data = { }
+ -- elseif n == 1 then
+ -- -- data = { result:fetch({},"a") }
+ else
+ data = { }
+ -- for i=1,n do
+ -- data[i] = result:fetch({},"a")
+ -- end
+ local k = #keys
+ for i=1,n do
+ local v = { result:fetch() }
+ local d = { }
+ for i=1,k do
+ d[keys[i]] = v[i]
+ end
+ data[#data+1] = d
end
- data[#data+1] = d
end
end
end
@@ -547,7 +589,7 @@ local function execute(specification)
end
end
if trace_sql then
- report_state("executing")
+ report_state("executing library")
end
if not validspecification(specification) then
report_state("error in specification")
@@ -558,7 +600,7 @@ local function execute(specification)
report_state("error in preparation")
return
end
- local data, keys = datafetched(specification,query)
+ local data, keys = datafetched(specification,query,specification.converter)
if not data then
report_state("error in fetching")
return
@@ -571,6 +613,7 @@ methods.library = {
execute = execute,
serialize = serialize,
deserialize = deserialize,
+ usesfiles = false,
}
-- -- --
@@ -590,6 +633,66 @@ end
sql.setmethod("client")
+-- helper:
+
+local execute = sql.execute
+
+function sql.usedatabase(presets,datatable)
+ local name = datatable or presets.datatable
+ if name then
+ local method = presets.method and sql.methods[presets.method] or sql.methods.client
+ local base = presets.database or "test"
+ local basename = format("`%s`.`%s`",base,name)
+ m_execute = execute
+ deserialize = deserialize
+ serialize = serialize
+ if method then
+ m_execute = method.execute or m_execute
+ deserialize = method.deserialize or deserialize
+ serialize = method.serialize or serialize
+ end
+ local execute
+ if method.usesfiles then
+ local queryfile = presets.queryfile or format("%s-temp.sql",name)
+ local resultfile = presets.resultfile or format("%s-temp.dat",name)
+ execute = function(specification) -- variables template
+ if not specification.presets then specification.presets = presets end
+ if not specification.queryfile then specification.queryfile = queryfile end
+ if not specification.resultfile then specification.resultfile = queryfile end
+ return m_execute(specification)
+ end
+ else
+ execute = function(specification) -- variables template
+ if not specification.presets then specification.presets = presets end
+ return m_execute(specification)
+ end
+ end
+ local function unpackdata(records,name)
+ if records then
+ name = name or "data"
+ for i=1,#records do
+ local record = records[i]
+ local data = record[name]
+ if data then
+ record[name] = deserialize(data)
+ end
+ end
+ end
+ end
+ return {
+ presets = preset,
+ base = base,
+ name = name,
+ basename = basename,
+ execute = execute,
+ serialize = serialize,
+ deserialize = deserialize,
+ unpackdata = unpackdata,
+ }
+ end
+end
+
+
-- local data = utilities.sql.prepare {
-- templatefile = "test.sql",
-- variables = { },
@@ -631,12 +734,92 @@ sql.setmethod("client")
sql.tokens = {
length = 42, -- but in practice we will reserve some 50 characters
new = function()
- return format("%s-%s",osuuid(),match(format("%x05",random(ostime())),".-(.....)$")) -- 36 + 1 + 5 = 42
- end, -- or random(0xFFFFF*osclock())
+ return format("%s-%x05",osuuid(),random(0xFFFFF)) -- 36 + 1 + 5 = 42
+ end,
}
-- -- --
+local converters = { }
+
+sql.converters = converters
+
+local template = [[
+local converters = utilities.sql.converters
+
+local tostring = tostring
+local tonumber = tonumber
+local toboolean = toboolean
+
+%s
+
+return function(result,deserialize)
+ if not result then
+ return { }
+ end
+ local nofrows = result:numrows() or 0
+ if nofrows == 0 then
+ return { }
+ end
+ local data = { }
+ for i=1,nofrows do
+ local v = { result:fetch() }
+ data[#data+1] = {
+ %s
+ }
+ end
+ return data
+end
+]]
+
+function sql.makeconverter(entries,deserialize)
+ local shortcuts = { }
+ local assignments = { }
+ for i=1,#entries do
+ local entry = entries[i]
+ local nam = entry.name
+ local typ = entry.type
+ if typ == "boolean" then
+ assignments[i] = format("[%q] = toboolean(v[%s],true),",nam,i)
+ elseif typ == "number" then
+ assignments[i] = format("[%q] = tonumber(v[%s]),",nam,i)
+ elseif type(typ) == "function" then
+ local c = #converters + 1
+ converters[c] = typ
+ shortcuts[#shortcuts+1] = format("local fun_%s = converters[%s]",c,c)
+ assignments[i] = format("[%q] = fun_%s(v[%s]),",nam,c,i)
+ elseif type(typ) == "table" then
+ local c = #converters + 1
+ converters[c] = typ
+ shortcuts[#shortcuts+1] = format("local tab_%s = converters[%s]",c,c)
+ assignments[i] = format("[%q] = tab_%s[v[%s]],",nam,#converters,i)
+ elseif typ == "deserialize" then
+ assignments[i] = format("[%q] = deserialize(v[%s]),",nam,i)
+ else
+ assignments[i] = format("[%q] = v[%s],",nam,i)
+ end
+ end
+ local code = string.format(template,table.concat(shortcuts,"\n"),table.concat(assignments,"\n "))
+ local func = loadstring(code)
+ if type(func) == "function" then
+ return func(), code
+ else
+ return false, code
+ end
+end
+
+-- local func, code = sql.makeconverter {
+-- { name = "a", type = "number" },
+-- { name = "b", type = "string" },
+-- { name = "c", type = "boolean" },
+-- { name = "d", type = { x = "1" } },
+-- { name = "e", type = os.fulltime },
+-- }
+--
+-- print(code)
+
+-- -- --
+
if tex and tex.systemmodes then
local droptable = table.drop
diff --git a/tex/context/base/util-tpl.lua b/tex/context/base/util-tpl.lua
index f6648b224..4cde1863b 100644
--- a/tex/context/base/util-tpl.lua
+++ b/tex/context/base/util-tpl.lua
@@ -6,10 +6,9 @@ if not modules then modules = { } end modules ['util-tpl'] = {
license = "see context related readme files"
}
--- experimental code
-
--- maybe make %% scanning optional
--- maybe use $[ and ]$ or {{ }}
+-- This is experimental code. Coming from dos and windows, I've always used %whatever%
+-- as template variables so let's stick to it. After all, it's easy to parse and stands
+-- out well. A double %% is turned into a regular %.
utilities.templates = utilities.templates or { }
local templates = utilities.templates
@@ -24,7 +23,7 @@ local P, C, Cs, Carg, lpegmatch = lpeg.P, lpeg.C, lpeg.Cs, lpeg.Carg, lpeg.match
local replacer
-local function replacekey(k,t)
+local function replacekey(k,t,recursive)
local v = t[k]
if not v then
if trace_template then
@@ -35,7 +34,11 @@ local function replacekey(k,t)
if trace_template then
report_template("setting key %q to value %q",k,v)
end
- return lpegmatch(replacer,v,1,t) -- recursive
+ if recursive then
+ return lpegmatch(replacer,v,1,t)
+ else
+ return v
+ end
end
end
@@ -56,9 +59,9 @@ local escapers = {
end,
}
-local function replacekeyunquoted(s,t,how) -- ".. \" "
+local function replacekeyunquoted(s,t,how,recurse) -- ".. \" "
local escaper = how and escapers[how] or escapers.lua
- return escaper(replacekey(s,t))
+ return escaper(replacekey(s,t,recurse))
end
local single = P("%") -- test %test% test : resolves test
@@ -72,15 +75,15 @@ local nodouble = double / ''
local nolquoted = lquoted / ''
local norquoted = rquoted / ''
-local key = nosingle * (C((1-nosingle)^1 * Carg(1))/replacekey) * nosingle
-local unquoted = nolquoted * ((C((1 - norquoted)^1) * Carg(1) * Carg(2))/replacekeyunquoted) * norquoted
+local key = nosingle * (C((1-nosingle)^1 * Carg(1) * Carg(2) * Carg(3))/replacekey) * nosingle
+local unquoted = nolquoted * ((C((1 - norquoted)^1) * Carg(1) * Carg(2) * Carg(3))/replacekeyunquoted) * norquoted
local any = P(1)
replacer = Cs((unquoted + escape + key + any)^0)
-local function replace(str,mapping,how)
+local function replace(str,mapping,how,recurse)
if mapping then
- return lpegmatch(replacer,str,1,mapping,how or "lua") or str
+ return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
else
return str
end
@@ -91,25 +94,24 @@ end
templates.replace = replace
-function templates.load(filename,mapping)
+function templates.load(filename,mapping,how,recurse)
local data = io.loaddata(filename) or ""
if mapping and next(mapping) then
- return replace(data,mapping)
+ return replace(data,mapping,how,recurse)
else
return data
end
end
-function templates.resolve(t,mapping)
+function templates.resolve(t,mapping,how,recurse)
if not mapping then
mapping = t
end
for k, v in next, t do
- t[k] = replace(v,mapping)
+ t[k] = replace(v,mapping,how,recurse)
end
return t
end
-- inspect(utilities.templates.replace("test %one% test", { one = "%two%", two = "two" }))
-- inspect(utilities.templates.resolve({ one = "%two%", two = "two", three = "%three%" }))
-
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index b40cb3eae..323c70c3e 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
-- merged file : luatex-fonts-merged.lua
-- parent file : luatex-fonts.lua
--- merge date : 09/16/12 23:18:22
+-- merge date : 09/21/12 20:58:19
do -- begin closure to overcome local limits and interference
@@ -1059,23 +1059,27 @@ function table.reversed(t)
end
end
-function table.sequenced(t,sep,simple) -- hash only
- local s, n = { }, 0
- for k, v in sortedhash(t) do
- if simple then
- if v == true then
- n = n + 1
- s[n] = k
- elseif v and v~= "" then
+function table.sequenced(t,sep) -- hash only
+ if t then
+ local s, n = { }, 0
+ for k, v in sortedhash(t) do
+ if simple then
+ if v == true then
+ n = n + 1
+ s[n] = k
+ elseif v and v~= "" then
+ n = n + 1
+ s[n] = k .. "=" .. tostring(v)
+ end
+ else
n = n + 1
s[n] = k .. "=" .. tostring(v)
end
- else
- n = n + 1
- s[n] = k .. "=" .. tostring(v)
end
+ return concat(s, sep or " | ")
+ else
+ return ""
end
- return concat(s, sep or " | ")
end
function table.print(t,...)