summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scripts/context/lua/mtxrun.lua99
-rw-r--r--scripts/context/stubs/mswin/mtxrun.lua99
-rw-r--r--scripts/context/stubs/unix/mtxrun99
-rw-r--r--tex/context/base/char-utf.lua8
-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.pdfbin4147 -> 4146 bytes
-rw-r--r--tex/context/base/context-version.pngbin106662 -> 104882 bytes
-rw-r--r--tex/context/base/context.mkii2
-rw-r--r--tex/context/base/context.mkiv2
-rw-r--r--tex/context/base/l-boolean.lua47
-rw-r--r--tex/context/base/l-unicode.lua26
-rw-r--r--tex/context/base/lxml-lpt.lua26
-rw-r--r--tex/context/base/math-ini.mkiv8
-rw-r--r--tex/context/base/status-files.pdfbin24572 -> 24499 bytes
-rw-r--r--tex/context/base/status-lua.pdfbin195090 -> 195100 bytes
-rw-r--r--tex/context/base/strc-num.lua13
-rw-r--r--tex/context/base/strc-sec.mkiv1
-rw-r--r--tex/context/base/syst-aux.mkiv24
-rw-r--r--tex/context/base/util-sql-imp-client.lua253
-rw-r--r--tex/context/base/util-sql-imp-library.lua282
-rw-r--r--tex/context/base/util-sql-imp-swiglib.lua419
-rw-r--r--tex/context/base/util-sql-users.lua96
-rw-r--r--tex/context/base/util-sql.lua721
-rw-r--r--tex/context/base/x-xfdf.mkiv1
-rw-r--r--tex/generic/context/luatex/luatex-fonts-merged.lua49
26 files changed, 1547 insertions, 732 deletions
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index 0d8f3ac50..93901f665 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -4133,30 +4133,49 @@ function boolean.tonumber(b)
end
function toboolean(str,tolerant)
- if str == true or str == false then
- return str
- elseif tolerant then
- local tstr = type(str)
- if tstr == "string" then
- return str == "true" or str == "yes" or str == "on" or str == "1" or str == "t"
- elseif tstr == "number" then
- return tonumber(str) ~= 0
- elseif tstr == "nil" then
- return false
- else
- return str
- end
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
elseif str == "true" then
return true
elseif str == "false" then
return false
+ elseif not tolerant then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
else
- return str
+ return str == "yes" or str == "on" or str == "t"
end
end
string.toboolean = toboolean
+function string.booleanstring(str)
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
+ elseif str == "true" then
+ return true
+ elseif str == "false" then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
+ else
+ return str == "yes" or str == "on" or str == "t"
+ end
+end
+
function string.is_boolean(str,default)
if type(str) == "string" then
if str == "true" or str == "yes" or str == "on" or str == "t" then
@@ -4217,34 +4236,38 @@ if not utf.char then
function utf.char(n)
if n < 0x80 then
+ -- 0aaaaaaa : 0x80
return char(n)
elseif n < 0x800 then
+ -- 110bbbaa : 0xC0 : n >> 6
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xC0 + floor(n/0x40),
0x80 + (n % 0x40)
)
elseif n < 0x10000 then
+ -- 1110bbbb : 0xE0 : n >> 12
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xE0 + floor(n/0x1000),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
- elseif n < 0x40000 then
+ elseif n < 0x200000 then
+ -- 11110ccc : 0xF0 : n >> 18
+ -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
+ -- dddd : ccccc - 1
return char(
- 0xF0 + floor(n/0x40000),
- 0x80 + floor(n/0x1000),
+ 0xF0 + floor(n/0x40000),
+ 0x80 + (floor(n/0x1000) % 0x40),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
else
- -- return char(
- -- 0xF1 + floor(n/0x1000000),
- -- 0x80 + floor(n/0x40000),
- -- 0x80 + floor(n/0x1000),
- -- 0x80 + (floor(n/0x40) % 0x40),
- -- 0x80 + (n % 0x40)
- -- )
- return "?"
+ return ""
end
end
@@ -10214,6 +10237,32 @@ function xml.inspect(collection,pattern)
end
end
+-- texy (see xfdf):
+
+local function split(e)
+ local dt = e.dt
+ if dt then
+ for i=1,#dt do
+ local dti = dt[i]
+ if type(dti) == "string" then
+ dti = gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
+ dti = gsub(dti,"[\n\r]+","\n\n")
+ dt[i] = dti
+ else
+ split(dti)
+ end
+ end
+ end
+ return e
+end
+
+function xml.finalizers.paragraphs(c)
+ for i=1,#c do
+ split(c[i])
+ end
+ return c
+end
+
end -- of closure
diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua
index 0d8f3ac50..93901f665 100644
--- a/scripts/context/stubs/mswin/mtxrun.lua
+++ b/scripts/context/stubs/mswin/mtxrun.lua
@@ -4133,30 +4133,49 @@ function boolean.tonumber(b)
end
function toboolean(str,tolerant)
- if str == true or str == false then
- return str
- elseif tolerant then
- local tstr = type(str)
- if tstr == "string" then
- return str == "true" or str == "yes" or str == "on" or str == "1" or str == "t"
- elseif tstr == "number" then
- return tonumber(str) ~= 0
- elseif tstr == "nil" then
- return false
- else
- return str
- end
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
elseif str == "true" then
return true
elseif str == "false" then
return false
+ elseif not tolerant then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
else
- return str
+ return str == "yes" or str == "on" or str == "t"
end
end
string.toboolean = toboolean
+function string.booleanstring(str)
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
+ elseif str == "true" then
+ return true
+ elseif str == "false" then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
+ else
+ return str == "yes" or str == "on" or str == "t"
+ end
+end
+
function string.is_boolean(str,default)
if type(str) == "string" then
if str == "true" or str == "yes" or str == "on" or str == "t" then
@@ -4217,34 +4236,38 @@ if not utf.char then
function utf.char(n)
if n < 0x80 then
+ -- 0aaaaaaa : 0x80
return char(n)
elseif n < 0x800 then
+ -- 110bbbaa : 0xC0 : n >> 6
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xC0 + floor(n/0x40),
0x80 + (n % 0x40)
)
elseif n < 0x10000 then
+ -- 1110bbbb : 0xE0 : n >> 12
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xE0 + floor(n/0x1000),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
- elseif n < 0x40000 then
+ elseif n < 0x200000 then
+ -- 11110ccc : 0xF0 : n >> 18
+ -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
+ -- dddd : ccccc - 1
return char(
- 0xF0 + floor(n/0x40000),
- 0x80 + floor(n/0x1000),
+ 0xF0 + floor(n/0x40000),
+ 0x80 + (floor(n/0x1000) % 0x40),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
else
- -- return char(
- -- 0xF1 + floor(n/0x1000000),
- -- 0x80 + floor(n/0x40000),
- -- 0x80 + floor(n/0x1000),
- -- 0x80 + (floor(n/0x40) % 0x40),
- -- 0x80 + (n % 0x40)
- -- )
- return "?"
+ return ""
end
end
@@ -10214,6 +10237,32 @@ function xml.inspect(collection,pattern)
end
end
+-- texy (see xfdf):
+
+local function split(e)
+ local dt = e.dt
+ if dt then
+ for i=1,#dt do
+ local dti = dt[i]
+ if type(dti) == "string" then
+ dti = gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
+ dti = gsub(dti,"[\n\r]+","\n\n")
+ dt[i] = dti
+ else
+ split(dti)
+ end
+ end
+ end
+ return e
+end
+
+function xml.finalizers.paragraphs(c)
+ for i=1,#c do
+ split(c[i])
+ end
+ return c
+end
+
end -- of closure
diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun
index 0d8f3ac50..93901f665 100644
--- a/scripts/context/stubs/unix/mtxrun
+++ b/scripts/context/stubs/unix/mtxrun
@@ -4133,30 +4133,49 @@ function boolean.tonumber(b)
end
function toboolean(str,tolerant)
- if str == true or str == false then
- return str
- elseif tolerant then
- local tstr = type(str)
- if tstr == "string" then
- return str == "true" or str == "yes" or str == "on" or str == "1" or str == "t"
- elseif tstr == "number" then
- return tonumber(str) ~= 0
- elseif tstr == "nil" then
- return false
- else
- return str
- end
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
elseif str == "true" then
return true
elseif str == "false" then
return false
+ elseif not tolerant then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
else
- return str
+ return str == "yes" or str == "on" or str == "t"
end
end
string.toboolean = toboolean
+function string.booleanstring(str)
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
+ elseif str == "true" then
+ return true
+ elseif str == "false" then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
+ else
+ return str == "yes" or str == "on" or str == "t"
+ end
+end
+
function string.is_boolean(str,default)
if type(str) == "string" then
if str == "true" or str == "yes" or str == "on" or str == "t" then
@@ -4217,34 +4236,38 @@ if not utf.char then
function utf.char(n)
if n < 0x80 then
+ -- 0aaaaaaa : 0x80
return char(n)
elseif n < 0x800 then
+ -- 110bbbaa : 0xC0 : n >> 6
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xC0 + floor(n/0x40),
0x80 + (n % 0x40)
)
elseif n < 0x10000 then
+ -- 1110bbbb : 0xE0 : n >> 12
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xE0 + floor(n/0x1000),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
- elseif n < 0x40000 then
+ elseif n < 0x200000 then
+ -- 11110ccc : 0xF0 : n >> 18
+ -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
+ -- dddd : ccccc - 1
return char(
- 0xF0 + floor(n/0x40000),
- 0x80 + floor(n/0x1000),
+ 0xF0 + floor(n/0x40000),
+ 0x80 + (floor(n/0x1000) % 0x40),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
else
- -- return char(
- -- 0xF1 + floor(n/0x1000000),
- -- 0x80 + floor(n/0x40000),
- -- 0x80 + floor(n/0x1000),
- -- 0x80 + (floor(n/0x40) % 0x40),
- -- 0x80 + (n % 0x40)
- -- )
- return "?"
+ return ""
end
end
@@ -10214,6 +10237,32 @@ function xml.inspect(collection,pattern)
end
end
+-- texy (see xfdf):
+
+local function split(e)
+ local dt = e.dt
+ if dt then
+ for i=1,#dt do
+ local dti = dt[i]
+ if type(dti) == "string" then
+ dti = gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
+ dti = gsub(dti,"[\n\r]+","\n\n")
+ dt[i] = dti
+ else
+ split(dti)
+ end
+ end
+ end
+ return e
+end
+
+function xml.finalizers.paragraphs(c)
+ for i=1,#c do
+ split(c[i])
+ end
+ return c
+end
+
end -- of closure
diff --git a/tex/context/base/char-utf.lua b/tex/context/base/char-utf.lua
index bd7463522..52fdfc0d0 100644
--- a/tex/context/base/char-utf.lua
+++ b/tex/context/base/char-utf.lua
@@ -486,6 +486,14 @@ if sequencers then
sequencers.enableaction(textfileactions,"characters.filters.utf.decompose")
end
+ directives.register("filters.utf.collapse", function(v)
+ sequencers[v and "enableaction" or "disableaction"](textfileactions,"characters.filters.utf.collapse")
+ end)
+
+ directives.register("filters.utf.decompose", function(v)
+ sequencers[v and "enableaction" or "disableaction"](textfileactions,"characters.filters.utf.decompose")
+ end)
+
end
--[[ldx--
diff --git a/tex/context/base/cont-new.mkii b/tex/context/base/cont-new.mkii
index d6ff107a3..6ceeafd46 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.25 21:44}
+\newcontextversion{2012.10.02 15:13}
%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 e8be14c7f..7604ff84e 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.25 21:44}
+\newcontextversion{2012.10.02 15:13}
%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 62b3117f7..07042d62c 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 91f5d5658..33ac9f216 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 db6a3d13d..0a1f16567 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.25 21:44}
+\edef\contextversion{2012.10.02 15:13}
%D For those who want to use this:
diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv
index c4d1a313b..f18a92afa 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.25 21:44}
+\edef\contextversion{2012.10.02 15:13}
%D For those who want to use this:
diff --git a/tex/context/base/l-boolean.lua b/tex/context/base/l-boolean.lua
index 4294cf8f0..2b94de76b 100644
--- a/tex/context/base/l-boolean.lua
+++ b/tex/context/base/l-boolean.lua
@@ -16,30 +16,49 @@ function boolean.tonumber(b)
end
function toboolean(str,tolerant)
- if str == true or str == false then
- return str
- elseif tolerant then
- local tstr = type(str)
- if tstr == "string" then
- return str == "true" or str == "yes" or str == "on" or str == "1" or str == "t"
- elseif tstr == "number" then
- return tonumber(str) ~= 0
- elseif tstr == "nil" then
- return false
- else
- return str
- end
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
elseif str == "true" then
return true
elseif str == "false" then
return false
+ elseif not tolerant then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
else
- return str
+ return str == "yes" or str == "on" or str == "t"
end
end
string.toboolean = toboolean
+function string.booleanstring(str)
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
+ elseif str == "true" then
+ return true
+ elseif str == "false" then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
+ else
+ return str == "yes" or str == "on" or str == "t"
+ end
+end
+
function string.is_boolean(str,default)
if type(str) == "string" then
if str == "true" or str == "yes" or str == "on" or str == "t" then
diff --git a/tex/context/base/l-unicode.lua b/tex/context/base/l-unicode.lua
index cbcec8329..630c34960 100644
--- a/tex/context/base/l-unicode.lua
+++ b/tex/context/base/l-unicode.lua
@@ -42,34 +42,38 @@ if not utf.char then
function utf.char(n)
if n < 0x80 then
+ -- 0aaaaaaa : 0x80
return char(n)
elseif n < 0x800 then
+ -- 110bbbaa : 0xC0 : n >> 6
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xC0 + floor(n/0x40),
0x80 + (n % 0x40)
)
elseif n < 0x10000 then
+ -- 1110bbbb : 0xE0 : n >> 12
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
return char(
0xE0 + floor(n/0x1000),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
- elseif n < 0x40000 then
+ elseif n < 0x200000 then
+ -- 11110ccc : 0xF0 : n >> 18
+ -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
+ -- dddd : ccccc - 1
return char(
- 0xF0 + floor(n/0x40000),
- 0x80 + floor(n/0x1000),
+ 0xF0 + floor(n/0x40000),
+ 0x80 + (floor(n/0x1000) % 0x40),
0x80 + (floor(n/0x40) % 0x40),
0x80 + (n % 0x40)
)
else
- -- return char(
- -- 0xF1 + floor(n/0x1000000),
- -- 0x80 + floor(n/0x40000),
- -- 0x80 + floor(n/0x1000),
- -- 0x80 + (floor(n/0x40) % 0x40),
- -- 0x80 + (n % 0x40)
- -- )
- return "?"
+ return ""
end
end
diff --git a/tex/context/base/lxml-lpt.lua b/tex/context/base/lxml-lpt.lua
index 3293300b4..0c10998a0 100644
--- a/tex/context/base/lxml-lpt.lua
+++ b/tex/context/base/lxml-lpt.lua
@@ -1366,3 +1366,29 @@ function xml.inspect(collection,pattern)
report_lpath("pattern %q\n\n%s\n",pattern,xml.tostring(e))
end
end
+
+-- texy (see xfdf):
+
+local function split(e)
+ local dt = e.dt
+ if dt then
+ for i=1,#dt do
+ local dti = dt[i]
+ if type(dti) == "string" then
+ dti = gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
+ dti = gsub(dti,"[\n\r]+","\n\n")
+ dt[i] = dti
+ else
+ split(dti)
+ end
+ end
+ end
+ return e
+end
+
+function xml.finalizers.paragraphs(c)
+ for i=1,#c do
+ split(c[i])
+ end
+ return c
+end
diff --git a/tex/context/base/math-ini.mkiv b/tex/context/base/math-ini.mkiv
index 641faf44a..77441e092 100644
--- a/tex/context/base/math-ini.mkiv
+++ b/tex/context/base/math-ini.mkiv
@@ -253,15 +253,15 @@
\def\utfmathcommand#1{\ctxcommand{utfmathcommand(\!!bs#1\!!es)}}
\def\utfmathfiller #1{\ctxcommand{utfmathfiller (\!!bs#1\!!es)}}
-\unexpanded\def\doifelseutfmathaccent#1{\ctxcommand{doifelseutfmathaccent("#1")}}
+\unexpanded\def\doifelseutfmathaccent#1{\ctxcommand{doifelseutfmathaccent(\!!bs#1\!!es)}}
%D Not used that much:
\installcorenamespace{mathcodecommand}
-\unexpanded\def\mathlimop #1{\mathop{#1}} %no \limits
-\unexpanded\def\mathbox #1{\dontleavehmode\hbox\Ustartmath\mathsurround\zeropoint#1\Ustopmath}
-\unexpanded\def\mathnolop #1{\mathop{#1}\nolimits}
+\unexpanded\def\mathlimop#1{\mathop{#1}} %no \limits
+\unexpanded\def\mathbox #1{\dontleavehmode\hbox\Ustartmath\mathsurround\zeropoint#1\Ustopmath}
+\unexpanded\def\mathnolop#1{\mathop{#1}\nolimits}
\let\mathnothing\firstofoneunexpanded
\let\mathalpha \firstofoneunexpanded
diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf
index 9e72bd5c2..c7d5a188a 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 1e5074cb6..9507ef95c 100644
--- a/tex/context/base/status-lua.pdf
+++ b/tex/context/base/status-lua.pdf
Binary files differ
diff --git a/tex/context/base/strc-num.lua b/tex/context/base/strc-num.lua
index 2fff8b3c5..b82132a00 100644
--- a/tex/context/base/strc-num.lua
+++ b/tex/context/base/strc-num.lua
@@ -388,7 +388,7 @@ function counters.setown(name,n,value)
local level = cd.level
if not level or level == -1 then
-- -1 is signal that we reset manually
- elseif level > 0 then
+ elseif level > 0 or level == -3 then
check(name,d,n+1)
elseif level == 0 then
-- happens elsewhere, check this for block
@@ -444,7 +444,7 @@ function counters.add(name,n,delta)
report_counters("adding, name: %s, level: text, action: checking",name)
end
check(name,data,n+1)
- elseif level > 0 then
+ elseif level > 0 or level == -3 then
-- within countergroup
if trace_counters then
report_counters("adding, name: %s, level: %s, action: checking within group",name,level)
@@ -468,9 +468,14 @@ end
function counters.check(level)
for name, cd in next, counterdata do
- if cd.level == level then
+ if level > 0 and cd.level == -3 then -- could become an option
if trace_counters then
- report_counters("resetting, name: %s, level: %s",name,level)
+ report_counters("resetting, name: %s, level: %s (head)",name,level)
+ end
+ reset(name)
+ elseif cd.level == level then
+ if trace_counters then
+ report_counters("resetting, name: %s, level: %s (normal)",name,level)
end
reset(name)
end
diff --git a/tex/context/base/strc-sec.mkiv b/tex/context/base/strc-sec.mkiv
index bc6e62372..826de59bf 100644
--- a/tex/context/base/strc-sec.mkiv
+++ b/tex/context/base/strc-sec.mkiv
@@ -478,6 +478,7 @@
\setvalue{\??headlevel\v!block}{0}
\setvalue{\??headlevel\v!none }{-1}
\setvalue{\??headlevel\v!text }{-2}
+\setvalue{\??headlevel\v!head }{-3}
\newtoks\everydefinesection
diff --git a/tex/context/base/syst-aux.mkiv b/tex/context/base/syst-aux.mkiv
index 815a26b33..058a251cb 100644
--- a/tex/context/base/syst-aux.mkiv
+++ b/tex/context/base/syst-aux.mkiv
@@ -2008,13 +2008,15 @@
%D us to do some checking, we reimplemented the non||empty
%D ones.
-\unexpanded\def\dosingleargument {\let\expectedarguments\plusone \dosingleempty }
-\unexpanded\def\dodoubleargument {\let\expectedarguments\plustwo \dodoubleempty }
-\unexpanded\def\dotripleargument {\let\expectedarguments\plusthree \dotripleempty }
-\unexpanded\def\doquadrupleargument {\let\expectedarguments\plusfour \doquadrupleempty }
-\unexpanded\def\doquintupleargument {\let\expectedarguments\plusfive \doquintupleempty }
-\unexpanded\def\dosixtupleargument {\let\expectedarguments\plussix \dosixtupleempty }
-\unexpanded\def\doseventupleargument{\let\expectedarguments\plusseven \doseventupleempty}
+% no longer a mesage:
+%
+% \unexpanded\def\dosingleargument {\let\expectedarguments\plusone \dosingleempty }
+% \unexpanded\def\dodoubleargument {\let\expectedarguments\plustwo \dodoubleempty }
+% \unexpanded\def\dotripleargument {\let\expectedarguments\plusthree \dotripleempty }
+% \unexpanded\def\doquadrupleargument {\let\expectedarguments\plusfour \doquadrupleempty }
+% \unexpanded\def\doquintupleargument {\let\expectedarguments\plusfive \doquintupleempty }
+% \unexpanded\def\dosixtupleargument {\let\expectedarguments\plussix \dosixtupleempty }
+% \unexpanded\def\doseventupleargument{\let\expectedarguments\plusseven \doseventupleempty}
%D \macros
%D {iffirstagument,ifsecondargument,ifthirdargument,
@@ -2576,6 +2578,14 @@
\def\syst_helpers_seventuple_empty_seven_spaced#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][] }
\def\syst_helpers_seventuple_empty_seven_normal#1#2#3#4#5#6#7{#1[{#2}][{#3}][{#4}][{#5}][{#6}][{#7}][]}
+\let\dosingleargument \dosingleempty
+\let\dodoubleargument \dodoubleempty
+\let\dotripleargument \dotripleempty
+\let\doquadrupleargument \doquadrupleempty
+\let\doquintupleargument \doquintupleempty
+\let\dosixtupleargument \dosixtupleempty
+\let\doseventupleargument\doseventupleempty
+
%D \macros
%D {strippedcsname}
%D
diff --git a/tex/context/base/util-sql-imp-client.lua b/tex/context/base/util-sql-imp-client.lua
new file mode 100644
index 000000000..8e174fc99
--- /dev/null
+++ b/tex/context/base/util-sql-imp-client.lua
@@ -0,0 +1,253 @@
+if not modules then modules = { } end modules ['util-sql-client'] = {
+ version = 1.001,
+ comment = "companion to util-sql.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: make a converter
+
+local rawset, setmetatable = rawset, setmetatable
+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 trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
+local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
+local report_state = logs.reporter("sql","client")
+
+local sql = require("util-sql")
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local preparetemplate = helpers.preparetemplate
+local splitdata = helpers.splitdata
+local replacetemplate = utilities.templates.replace
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+
+-- Experiments with an p/action demonstrated that there is not much gain. We could do a runtime
+-- capture but creating all the small tables is not faster and it doesn't work well anyway.
+
+local separator = P("\t")
+local newline = patterns.newline
+local empty = Cc("")
+
+local entry = C((1-separator-newline)^0) -- C 10% faster than Cs
+
+local unescaped = P("\\n") / "\n"
+ + P("\\t") / "\t"
+ + P("\\0") / "\000"
+ + P("\\\\") / "\\"
+
+local entry = Cs((unescaped + (1-separator-newline))^0) -- C 10% faster than Cs but Cs needed due to nesting
+
+local getfirst = Ct( entry * (separator * (entry+empty))^0) + newline
+local skipfirst = (1-newline)^1 * newline
+local getfirstline = C((1-newline)^0)
+
+local cache = { }
+
+local function splitdata(data) -- todo: hash on first line ... maybe move to client module
+ if data == "" then
+ if trace_sql then
+ report_state("no data")
+ end
+ return { }, { }
+ end
+ local first = lpegmatch(getfirstline,data)
+ if not first then
+ if trace_sql then
+ report_state("no data")
+ end
+ return { }, { }
+ end
+ local p = cache[first]
+ if p then
+ -- report_state("reusing: %s",first)
+ local entries = lpegmatch(p.parser,data)
+ return entries or { }, p.keys
+ elseif p == false then
+ return { }, { }
+ elseif p == nil then
+ local keys = lpegmatch(getfirst,first) or { }
+ if #keys == 0 then
+ if trace_sql then
+ report_state("no banner")
+ end
+ cache[first] = false
+ return { }, { }
+ end
+ -- quite generic, could be a helper
+ local n = #keys
+ if n == 0 then
+ report_state("no fields")
+ cache[first] = false
+ return { }, { }
+ end
+ if n == 1 then
+ local key = keys[1]
+ if trace_sql then
+ report_state("one field with name",key)
+ end
+ p = Cg(Cc(key) * entry)
+ else
+ for i=1,n do
+ local key = keys[i]
+ if trace_sql then
+ report_state("field %s has name %q",i,key)
+ end
+ local s = Cg(Cc(key) * entry)
+ if p then
+ p = p * separator * s
+ else
+ p = s
+ end
+ end
+ end
+ p = Cf(Ct("") * p,rawset) * newline^1
+ p = skipfirst * Ct(p^0)
+ cache[first] = { parser = p, keys = keys }
+ local entries = lpegmatch(p,data)
+ return entries or { }, keys
+ end
+end
+
+local splitter = skipfirst * Ct((Ct(entry * (separator * entry)^0) * newline^1)^0)
+
+local function getdata(data)
+ return lpegmatch(splitter,data)
+end
+
+helpers.splitdata = splitdata
+helpers.getdata = getdata
+
+local function dataprepared(specification)
+ local query = preparetemplate(specification)
+ if query then
+ io.savedata(specification.queryfile,query)
+ os.remove(specification.resultfile)
+ if trace_queries then
+ report_state("query: %s",query)
+ end
+ return true
+ else
+ -- maybe push an error
+ os.remove(specification.queryfile)
+ os.remove(specification.resultfile)
+ end
+end
+
+local function datafetched(specification,runner)
+ local command = replacetemplate(runner,specification)
+ if trace_sql then
+ local t = osclock()
+ report_state("command: %s",command)
+ local okay = os.execute(command)
+ report_state("fetchtime: %.3f sec",osclock()-t) -- not okay under linux
+ return okay == 0
+ else
+ return os.execute(command) == 0
+ end
+end
+
+local function dataloaded(specification)
+ if trace_sql then
+ local t = osclock()
+ local data = io.loaddata(specification.resultfile) or ""
+ report_state("datasize: %.3f MB",#data/1024/1024)
+ report_state("loadtime: %.3f sec",osclock()-t)
+ return data
+ else
+ return io.loaddata(specification.resultfile) or ""
+ end
+end
+
+local function dataconverted(data,converter)
+ if converter then
+ local data = getdata(data)
+ if data then
+ converter.client(data)
+ end
+ return data
+ elseif trace_sql then
+ local t = osclock()
+ local data, keys = splitdata(data)
+ report_state("converttime: %.3f",osclock()-t)
+ report_state("keys: %s ",#keys)
+ report_state("entries: %s ",#data)
+ return data, keys
+ else
+ return splitdata(data)
+ end
+end
+
+-- todo: new, etc
+
+local function execute(specification)
+ if trace_sql then
+ report_state("executing client")
+ end
+ if not validspecification(specification) then
+ report_state("error in specification")
+ return
+ end
+ if not dataprepared(specification) then
+ report_state("error in preparation")
+ return
+ end
+ if not datafetched(specification,methods.client.runner) then
+ report_state("error in fetching, query: %s",string.collapsespaces(io.loaddata(specification.queryfile)))
+ return
+ end
+ local data = dataloaded(specification)
+ if not data then
+ report_state("error in loading")
+ return
+ end
+ local data, keys = dataconverted(data,specification.converter)
+ if not data then
+ report_state("error in converting")
+ return
+ end
+ local one = data[1]
+ if one then
+ setmetatable(data,{ __index = one } )
+ end
+ return data, keys
+end
+
+-- The following is not that (memory) efficient but normally we will use
+-- the lib anyway. Of course we could make a dedicated converter and/or
+-- hook into the splitter code but ... it makes not much sense because then
+-- we can as well move the builder to the library modules.
+
+local wraptemplate = [[
+local converters = utilities.sql.converters
+local deserialize = utilities.sql.deserialize
+
+local tostring = tostring
+local tonumber = tonumber
+local booleanstring = string.booleanstring
+
+%s
+
+return function(data)
+ for i=1,#data do
+ local cells = data[i]
+ data[i] = {
+ %s
+ }
+ end
+ return data
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.client = {
+ runner = [[mysql --batch --user="%username%" --password="%password%" --host="%host%" --port=%port% --database="%database%" --default-character-set=utf8 < "%queryfile%" > "%resultfile%"]],
+ execute = execute,
+ usesfiles = true,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
diff --git a/tex/context/base/util-sql-imp-library.lua b/tex/context/base/util-sql-imp-library.lua
new file mode 100644
index 000000000..e04c4ac44
--- /dev/null
+++ b/tex/context/base/util-sql-imp-library.lua
@@ -0,0 +1,282 @@
+if not modules then modules = { } end modules ['util-sql-library'] = {
+ version = 1.001,
+ comment = "companion to util-sql.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- For some reason the sql lib partially fails in luatex when creating hashed row. So far
+-- we couldn't figure it out (some issue with adapting the table that is passes as first
+-- argument in the fetch routine. Apart from this it looks like the mysql binding has some
+-- 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:
+
+-- local function collect_1(r)
+-- local t = { }
+-- for i=1,r:numrows() do
+-- t[#t+1] = r:fetch({},"a")
+-- end
+-- return t
+-- end
+--
+-- local function collect_2(r)
+-- local keys = r:getcolnames()
+-- local n = #keys
+-- local t = { }
+-- for i=1,r:numrows() do
+-- local v = { r:fetch() }
+-- local r = { }
+-- for i=1,n do
+-- r[keys[i]] = v[i]
+-- end
+-- t[#t+1] = r
+-- end
+-- return t
+-- end
+--
+-- local function collect_3(r)
+-- local keys = r:getcolnames()
+-- local n = #keys
+-- local t = { }
+-- for i=1,r:numrows() do
+-- local v = r:fetch({},"n")
+-- local r = { }
+-- for i=1,n do
+-- r[keys[i]] = v[i]
+-- end
+-- t[#t+1] = r
+-- end
+-- return t
+-- end
+--
+-- On a large table with some 8 columns (mixed text and numbers) we get the following
+-- timings (the 'a' alternative is already using the more efficient variant in the
+-- binding).
+--
+-- collect_1 : 1.31
+-- collect_2 : 1.39
+-- collect_3 : 1.75
+--
+-- Some, as a workaround for this 'bug' the second alternative can be used.
+
+local format = string.format
+local lpegmatch = lpeg.match
+local setmetatable, type = setmetatable, type
+
+local trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
+local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
+local report_state = logs.reporter("sql","library")
+
+local sql = require("util-sql")
+local mysql = require("luasql.mysql")
+local cache = { }
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local querysplitter = helpers.querysplitter
+local dataprepared = helpers.preparetemplate
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+
+local initialize = mysql.mysql
+
+local function connect(session,specification)
+ return session:connect(
+ specification.database or "",
+ specification.username or "",
+ specification.password or "",
+ specification.host or "",
+ specification.port
+ )
+end
+
+local function datafetched(specification,query,converter)
+ if not query or query == "" then
+ report_state("no valid query")
+ return { }, { }
+ end
+ local id = specification.id
+ local session, connection
+ if id then
+ local c = cache[id]
+ if c then
+ session = c.session
+ connection = c.connection
+ end
+ if not connection then
+ session = initialize()
+ connection = connect(session,specification)
+ cache[id] = { session = session, connection = connection }
+ end
+ else
+ session = initialize()
+ 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(querysplitter,query)
+ local result, message, okay
+ for i=1,#query do
+ local q = query[i]
+ 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 okay and id then -- can go
+ if session then
+ session:close()
+ end
+ if connection then
+ connection:close()
+ end
+ session = initialize() -- maybe not needed
+ connection = connect(session,specification)
+ if connection then
+ cache[id] = { session = session, connection = connection }
+ for i=1,#query do
+ local q = query[i]
+ 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
+ else
+ message = "unable to connect"
+ report_state(message)
+ end
+ end
+ local data, keys
+ if result then
+ if converter then
+ data = converter.library(result)
+ -- data = converter(result)
+ 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
+ end
+ end
+ end
+ result:close()
+ elseif message then
+ report_state("message %s",message)
+ end
+ if not keys then
+ keys = { }
+ end
+ if not data then
+ data = { }
+ end
+ if not id then
+ connection:close()
+ session:close()
+ end
+ return data, keys
+end
+
+local function execute(specification)
+ if trace_sql then
+ report_state("executing library")
+ end
+ if not validspecification(specification) then
+ report_state("error in specification")
+ return
+ end
+ local query = dataprepared(specification)
+ if not query then
+ report_state("error in preparation")
+ return
+ end
+ local data, keys = datafetched(specification,query,specification.converter)
+ if not data then
+ report_state("error in fetching")
+ return
+ end
+ local one = data[1]
+ if one then
+ setmetatable(data,{ __index = one } )
+ end
+ return data, keys
+end
+
+local wraptemplate = [[
+local converters = utilities.sql.converters
+local deserialize = utilities.sql.deserialize
+
+local tostring = tostring
+local tonumber = tonumber
+local booleanstring = string.booleanstring
+
+%s
+
+return function(result)
+ 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 cells = { result:fetch() }
+ data[i] = {
+ %s
+ }
+ end
+ return data
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.library = {
+ runner = function() end, -- never called
+ execute = execute,
+ usesfiles = false,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
diff --git a/tex/context/base/util-sql-imp-swiglib.lua b/tex/context/base/util-sql-imp-swiglib.lua
new file mode 100644
index 000000000..4b9cda896
--- /dev/null
+++ b/tex/context/base/util-sql-imp-swiglib.lua
@@ -0,0 +1,419 @@
+if not modules then modules = { } end modules ['util-sql-swiglib'] = {
+ version = 1.001,
+ comment = "companion to util-sql.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- As the regular library is flawed (i.e. there are crashes in the table
+-- construction code) and also not that efficient, Luigi Scarso looked into
+-- a swig binding. This is a bit more low level approach but as we stay
+-- closer to the original library it's also less dependant.
+
+local concat = table.concat
+local format = string.format
+local lpegmatch = lpeg.match
+local setmetatable, type = setmetatable, type
+
+local trace_sql = false trackers.register("sql.trace", function(v) trace_sql = v end)
+local trace_queries = false trackers.register("sql.queries",function(v) trace_queries = v end)
+local report_state = logs.reporter("sql","swiglib")
+
+local sql = require("util-sql")
+local mysql = require("swigluamysql")
+local cache = { }
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local querysplitter = helpers.querysplitter
+local dataprepared = helpers.preparetemplate
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+
+local mysql_initialize = mysql.mysql_init
+
+local mysql_open_connection = mysql.mysql_real_connect
+local mysql_execute_query = mysql.mysql_real_query
+local mysql_close_connection = mysql.mysql_close
+
+local mysql_field_seek = mysql.mysql_field_seek
+local mysql_num_fields = mysql.mysql_num_fields
+local mysql_fetch_field = mysql.mysql_fetch_field
+local mysql_num_rows = mysql.mysql_num_rows
+local mysql_fetch_row = mysql.mysql_fetch_row
+local mysql_fetch_lengths = mysql.mysql_fetch_lengths
+local mysql_init = mysql.mysql_init
+local mysql_store_result = mysql.mysql_store_result
+local mysql_free_result = mysql.mysql_free_result
+local mysql_use_result = mysql.mysql_use_result
+
+local mysql_error_message = mysql.mysql_error
+local mysql_options_argument = mysql.mysql_options_argument
+
+local instance = mysql.MYSQL()
+
+local mysql_constant_false = mysql_options_argument(false) -- 0 "\0"
+local mysql_constant_true = mysql_options_argument(true) -- 1 "\1"
+
+-- print(swig_type(mysql_constant_false))
+-- print(swig_type(mysql_constant_true))
+
+mysql.mysql_options(instance,mysql.MYSQL_OPT_RECONNECT,mysql_constant_true);
+
+local typemap = {
+ [mysql.MYSQL_TYPE_VAR_STRING ] = "string",
+ [mysql.MYSQL_TYPE_STRING ] = "string",
+ [mysql.MYSQL_TYPE_DECIMAL ] = "number",
+ [mysql.MYSQL_TYPE_SHORT ] = "number",
+ [mysql.MYSQL_TYPE_LONG ] = "number",
+ [mysql.MYSQL_TYPE_FLOAT ] = "number",
+ [mysql.MYSQL_TYPE_DOUBLE ] = "number",
+ [mysql.MYSQL_TYPE_LONGLONG ] = "number",
+ [mysql.MYSQL_TYPE_INT24 ] = "number",
+ [mysql.MYSQL_TYPE_YEAR ] = "number",
+ [mysql.MYSQL_TYPE_TINY ] = "number",
+ [mysql.MYSQL_TYPE_TINY_BLOB ] = "binary",
+ [mysql.MYSQL_TYPE_MEDIUM_BLOB] = "binary",
+ [mysql.MYSQL_TYPE_LONG_BLOB ] = "binary",
+ [mysql.MYSQL_TYPE_BLOB ] = "binary",
+ [mysql.MYSQL_TYPE_DATE ] = "date",
+ [mysql.MYSQL_TYPE_NEWDATE ] = "date",
+ [mysql.MYSQL_TYPE_DATETIME ] = "datetime",
+ [mysql.MYSQL_TYPE_TIME ] = "time",
+ [mysql.MYSQL_TYPE_TIMESTAMP ] = "time",
+ [mysql.MYSQL_TYPE_ENUM ] = "set",
+ [mysql.MYSQL_TYPE_SET ] = "set",
+ [mysql.MYSQL_TYPE_NULL ] = "null",
+}
+
+-- real_escape_string
+
+local function finish(t)
+ mysql_free_result(t._result_)
+end
+
+-- will become metatable magic
+
+-- local function analyze(result)
+-- mysql_field_seek(result,0)
+-- local nofrows = mysql_num_rows(result) or 0
+-- local noffields = mysql_num_fields(result)
+-- local names = { }
+-- local types = { }
+-- for i=1,noffields do
+-- local field = mysql_fetch_field(result)
+-- names[i] = field.name
+-- types[i] = field.type
+-- end
+-- return names, types, noffields, nofrows
+-- end
+
+local function getcolnames(t)
+ return t.names
+end
+
+local function getcoltypes(t)
+ return t.types
+end
+
+local function numrows(t)
+ return t.nofrows
+end
+
+-- swig_type
+
+-- local ulongArray_getitem = mysql.ulongArray_getitem
+-- local util_getbytearray = mysql.util_getbytearray
+
+-- local function list(t)
+-- local result = t._result_
+-- local row = mysql_fetch_row(result)
+-- local len = mysql_fetch_lengths(result)
+-- local result = { }
+-- for i=1,t.noffields do
+-- local r = i - 1 -- zero offset
+-- result[i] = util_getbytearray(row,r,ulongArray_getitem(len,r))
+-- end
+-- return result
+-- end
+
+-- local function hash(t)
+-- local list = util_mysql_fetch_fields_from_current_row(t._result_)
+-- local result = t._result_
+-- local fields = t.names
+-- local row = mysql_fetch_row(result)
+-- local len = mysql_fetch_lengths(result)
+-- local result = { }
+-- for i=1,t.noffields do
+-- local r = i - 1 -- zero offset
+-- result[fields[i]] = util_getbytearray(row,r,ulongArray_getitem(len,r))
+-- end
+-- return result
+-- end
+
+local util_mysql_fetch_fields_from_current_row = mysql.util_mysql_fetch_fields_from_current_row
+
+local function list(t)
+ return util_mysql_fetch_fields_from_current_row(t._result_)
+end
+
+local function hash(t)
+ local list = util_mysql_fetch_fields_from_current_row(t._result_)
+ local fields = t.names
+ local data = { }
+ for i=1,t.noffields do
+ data[fields[i]] = list[i]
+ end
+ return data
+end
+
+local mt = { __index = {
+ -- regular
+ finish = finish,
+ list = list,
+ hash = hash,
+ -- compatibility
+ numrows = numrows,
+ getcolnames = getcolnames,
+ getcoltypes = getcoltypes,
+ }
+}
+
+-- session
+
+local function close(t)
+ mysql_close_connection(t._connection_)
+end
+
+local function execute(t,query)
+ if query and query ~= "" then
+ local connection = t._connection_
+ local result = mysql_execute_query(connection,query,#query)
+ if result == 0 then
+ local result = mysql_store_result(connection)
+ mysql_field_seek(result,0)
+ local nofrows = mysql_num_rows(result) or 0
+ local noffields = mysql_num_fields(result)
+ local names = { }
+ local types = { }
+ for i=1,noffields do
+ local field = mysql_fetch_field(result)
+ names[i] = field.name
+ types[i] = field.type
+ end
+ local t = {
+ _result_ = result,
+ names = names,
+ types = types,
+ noffields = noffields,
+ nofrows = nofrows,
+ }
+ return setmetatable(t,mt)
+ end
+ end
+ return false
+end
+
+local mt = { __index = {
+ close = close,
+ execute = execute,
+ }
+}
+
+local function open(t,database,username,password,host,port)
+ local connection = mysql_open_connection(t._session_,host or "localhost",username or "",password or "",database or "",port or 0,0,0)
+ if connection then
+ local t = {
+ _connection_ = connection,
+ }
+ return setmetatable(t,mt)
+ end
+end
+
+local function message(t)
+ return mysql_error_message(t._session_)
+end
+
+local function close(t)
+ -- dummy, as we have a global session
+end
+
+local mt = {
+ __index = {
+ connect = open,
+ close = close,
+ message = message,
+ }
+}
+
+local function initialize()
+ local session = {
+ _session_ = mysql_initialize(instance) -- maybe share, single thread anyway
+ }
+ return setmetatable(session,mt)
+end
+
+-- -- -- --
+
+local function connect(session,specification)
+ return session:connect(
+ specification.database or "",
+ specification.username or "",
+ specification.password or "",
+ specification.host or "",
+ specification.port
+ )
+end
+
+local function datafetched(specification,query,converter)
+ if not query or query == "" then
+ report_state("no valid query")
+ return { }, { }
+ end
+ local id = specification.id
+ local session, connection
+ if id then
+ local c = cache[id]
+ if c then
+ session = c.session
+ connection = c.connection
+ end
+ if not connection then
+ session = initialize()
+ connection = connect(session,specification)
+ cache[id] = { session = session, connection = connection }
+ end
+ else
+ session = initialize()
+ 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(querysplitter,query)
+ local result, message, okay
+ for i=1,#query do
+ local q = query[i]
+ local r, m = connection:execute(q)
+ if m then
+ report_state("error in query, stage: %s",string.collapsespaces(q))
+ message = message and format("%s\n%s",message,m) or m
+ end
+ if type(r) == "table" then
+ result = r
+ okay = true
+ elseif not m then
+ okay = true
+ end
+ end
+ local data, keys
+ if result then
+ if converter then
+ data = converter.swiglib(result)
+ else
+ keys = result.names
+ data = { }
+ for i=1,result.nofrows do
+ data[i] = result:hash()
+ end
+ end
+ result:finish() -- result:close()
+ elseif message then
+ report_state("message %s",message)
+ end
+ if not keys then
+ keys = { }
+ end
+ if not data then
+ data = { }
+ end
+ if not id then
+ connection:close()
+ session:close()
+ end
+ return data, keys
+end
+
+local function execute(specification)
+ if trace_sql then
+ report_state("executing library")
+ end
+ if not validspecification(specification) then
+ report_state("error in specification")
+ return
+ end
+ local query = dataprepared(specification)
+ if not query then
+ report_state("error in preparation")
+ return
+ end
+ local data, keys = datafetched(specification,query,specification.converter)
+ if not data then
+ report_state("error in fetching")
+ return
+ end
+ local one = data[1]
+ if one then
+ setmetatable(data,{ __index = one } )
+ end
+ return data, keys
+end
+
+local wraptemplate = [[
+local mysql = require("swigluamysql") -- will be stored in method
+
+----- mysql_fetch_row = mysql.mysql_fetch_row
+----- mysql_fetch_lengths = mysql.mysql_fetch_lengths
+----- util_unpackbytearray = mysql.util_unpackbytearray
+local util_mysql_fetch_fields_from_current_row
+ = mysql.util_mysql_fetch_fields_from_current_row
+
+local converters = utilities.sql.converters
+local deserialize = utilities.sql.deserialize
+
+local tostring = tostring
+local tonumber = tonumber
+local booleanstring = string.booleanstring
+
+%s
+
+return function(result)
+ if not result then
+ return { }
+ end
+ local nofrows = result.nofrows or 0
+ if nofrows == 0 then
+ return { }
+ end
+ local noffields = result.noffields or 0
+ local data = { }
+ result = result._result_
+ for i=1,nofrows do
+ -- local row = mysql_fetch_row(result)
+ -- local len = mysql_fetch_lengths(result)
+ -- local cells = util_unpackbytearray(row,noffields,len)
+ local cells = util_mysql_fetch_fields_from_current_row(result)
+ data[i] = {
+ %s
+ }
+ end
+ return data
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.swiglib = {
+ runner = function() end, -- never called
+ execute = execute,
+ usesfiles = false,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
diff --git a/tex/context/base/util-sql-users.lua b/tex/context/base/util-sql-users.lua
index 4389b905c..81324afcf 100644
--- a/tex/context/base/util-sql-users.lua
+++ b/tex/context/base/util-sql-users.lua
@@ -13,8 +13,9 @@ if not modules then modules = { } end modules ['util-sql-users'] = {
local sql = require("util-sql")
local md5 = require("md5")
-local format, upper, find, gsub = string.format, string.upper, string.find, string.gsub
+local format, upper, find, gsub, escapedpattern = string.format, string.upper, string.find, string.gsub, string.escapedpattern
local sumhexa = md5.sumhexa
+local booleanstring = string.booleanstring
local users = { }
sql.users = users
@@ -37,6 +38,9 @@ local function cleanuppassword(str)
end
local function samepasswords(one,two)
+ if not one or not two then
+ return false
+ end
if not find(one,"^MD5:") then
one = encryptpassword(one)
end
@@ -46,9 +50,22 @@ local function samepasswords(one,two)
return one == two
end
+local function validaddress(address,addresses)
+ if address and addresses and address ~= "" and addresses ~= "" then
+ if find(address,"^" .. escapedpattern(addresses,true)) then -- simple escapes
+ return true, "valid remote address"
+ end
+ return false, "invalid remote address"
+ else
+ return true, "no remote address check"
+ end
+end
+
+
users.encryptpassword = encryptpassword
users.cleanuppassword = cleanuppassword
users.samepasswords = samepasswords
+users.validaddress = validaddress
-- print(users.encryptpassword("test")) -- MD5:098F6BCD4621D373CADE4E832627B4F6
@@ -86,13 +103,14 @@ users.groupnumbers = groupnumbers
local template =[[
CREATE TABLE `users` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `name` varchar(80) NOT NULL,
- `password` varbinary(50) DEFAULT NULL,
- `group` int(11) NOT NULL,
- `enabled` int(11) DEFAULT '1',
- `email` varchar(80) DEFAULT NULL,
- `data` longtext,
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(80) NOT NULL,
+ `password` varchar(50) DEFAULT NULL,
+ `group` int(11) NOT NULL,
+ `enabled` int(11) DEFAULT '1',
+ `email` varchar(80) DEFAULT NULL,
+ `address` varchar(256) DEFAULT NULL,
+ `data` longtext,
PRIMARY KEY (`id`),
UNIQUE KEY `name_unique` (`name`)
) DEFAULT CHARSET = utf8 ;
@@ -105,6 +123,7 @@ local converter, fields = sql.makeconverter {
{ name = "group", type = groupnames },
{ name = "enabled", type = "boolean" },
{ name = "email", type = "string" },
+ { name = "address", type = "string" },
{ name = "data", type = "deserialize" },
}
@@ -137,7 +156,43 @@ local template =[[
;
]]
-function users.valid(db,username,password)
+-- function users.valid(db,username,password)
+--
+-- local data = db.execute {
+-- template = template,
+-- converter = converter,
+-- variables = {
+-- basename = db.basename,
+-- fields = fields,
+-- name = username,
+-- password = encryptpassword(password),
+-- },
+-- }
+--
+-- local data = data and data[1]
+--
+-- if not data then
+-- return false, "unknown"
+-- elseif not data.enabled then
+-- return false, "disabled"
+-- else
+-- data.password = nil
+-- return data, "okay"
+-- end
+--
+-- end
+
+local template =[[
+ SELECT
+ %fields%
+ FROM
+ %basename%
+ WHERE
+ `name` = '%name%'
+ ;
+]]
+
+function users.valid(db,username,password,address)
local data = db.execute {
template = template,
@@ -146,16 +201,19 @@ function users.valid(db,username,password)
basename = db.basename,
fields = fields,
name = username,
- password = encryptpassword(password),
},
}
local data = data and data[1]
if not data then
- return false, "unknown"
+ return false, "unknown user"
elseif not data.enabled then
- return false, "disabled"
+ return false, "disabled user"
+ elseif data.password ~= encryptpassword(password) then
+ return false, "wrong password"
+ elseif not validaddress(address,data.address) then
+ return false, "invalid address"
else
data.password = nil
return data, "okay"
@@ -170,6 +228,7 @@ local template =[[
`group`,
`enabled`,
`email`,
+ `address`,
`data`
) VALUES (
'%name%',
@@ -177,6 +236,7 @@ local template =[[
'%group%',
'%enabled%',
'%email%',
+ '%address%',
'%[data]%'
) ;
]]
@@ -189,6 +249,8 @@ function users.add(db,specification)
return
end
+ local data = specification.data
+
db.execute {
template = template,
variables = {
@@ -196,9 +258,10 @@ function users.add(db,specification)
name = name,
password = encryptpassword(specification.password or ""),
group = groupnumbers[specification.group] or groupnumbers.guest,
- enabled = toboolean(specification.enabled,true) and "1" or "0",
+ enabled = booleanstring(specification.enabled) and "1" or "0",
email = specification.email,
- data = type(specification.data) == "table" and db.serialize(specification.data,"return") or "",
+ address = specification.address,
+ data = type(data) == "table" and db.serialize(data,"return") or "",
},
}
@@ -264,6 +327,7 @@ local template =[[
`group` = '%group%',
`enabled` = '%enabled%',
`email` = '%email%',
+ `address` = '%address%',
`data` = '%[data]%'
WHERE
`id` = '%id%'
@@ -286,6 +350,7 @@ function users.save(db,id,specification)
local group = specification.group == nil and user.group or specification.group
local enabled = specification.enabled == nil and user.enabled or specification.enabled
local email = specification.email == nil and user.email or specification.email
+ local address = specification.address == nil and user.address or specification.address
local data = specification.data == nil and user.data or specification.data
-- table.print(data)
@@ -297,8 +362,9 @@ function users.save(db,id,specification)
id = id,
password = encryptpassword(password),
group = groupnumbers[group],
- enabled = toboolean(enabled,true) and "1" or "0",
+ enabled = booleanstring(enabled) and "1" or "0",
email = email,
+ address = address,
data = type(data) == "table" and db.serialize(data,"return") or "",
},
}
diff --git a/tex/context/base/util-sql.lua b/tex/context/base/util-sql.lua
index 4e9a5bcd4..e2edf0ffe 100644
--- a/tex/context/base/util-sql.lua
+++ b/tex/context/base/util-sql.lua
@@ -19,62 +19,42 @@ if not modules then modules = { } end modules ['util-sql'] = {
-- context in a regular tds tree (the standard distribution) it makes sense to put shared
-- code in the context distribution. That way we don't need to reinvent wheels every time.
--- For some reason the sql lib partially fails in luatex when creating hashed row. So far
--- we couldn't figure it out (some issue with adapting the table that is passes as first
--- argument in the fetch routine. Apart from this it looks like the mysql binding has some
--- 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
--- t[#t+1] = r:fetch({},"a")
--- end
--- return t
--- end
---
--- local function collect_2(r)
--- local keys = r:getcolnames()
--- local n = #keys
--- local t = { }
--- for i=1,r:numrows() do
--- local v = { r:fetch() }
--- local r = { }
--- for i=1,n do
--- r[keys[i]] = v[i]
--- end
--- t[#t+1] = r
--- end
--- return t
--- end
+-- util-sql-imp-client.lua
+-- util-sql-imp-library.lua
+-- util-sql-imp-swiglib.lua
+-- util-sql-imp-lmxsql.lua
+
+-- local sql = require("util-sql")
--
--- local function collect_3(r)
--- local keys = r:getcolnames()
--- local n = #keys
--- local t = { }
--- for i=1,r:numrows() do
--- local v = r:fetch({},"n")
--- local r = { }
--- for i=1,n do
--- r[keys[i]] = v[i]
--- end
--- t[#t+1] = r
--- end
--- return t
--- end
+-- local converter = sql.makeconverter {
+-- { name = "id", type = "number" },
+-- { name = "data",type = "string" },
+-- }
--
--- On a large table with some 8 columns (mixed text and numbers) we get the following
--- timings (the 'a' alternative is already using the more efficient variant in the
--- binding).
+-- local execute = sql.methods.swiglib.execute
+-- -- local execute = sql.methods.library.execute
+-- -- local execute = sql.methods.client.execute
+-- -- local execute = sql.methods.lmxsql.execute
--
--- collect_1 : 1.31
--- collect_2 : 1.39
--- collect_3 : 1.75
+-- result = execute {
+-- presets = {
+-- host = "localhost",
+-- username = "root",
+-- password = "test",
+-- database = "test",
+-- id = "test", -- forces persistent session
+-- },
+-- template = "select * from `test` where `id` > %criterium% ;",
+-- variables = {
+-- criterium = 2,
+-- },
+-- converter = converter
+-- }
--
--- Some, as a workaround for this 'bug' the second alternative can be used.
+-- inspect(result)
local format, match = string.format, string.match
local random = math.random
@@ -102,12 +82,18 @@ local loadtemplate = utilities.templates.load
local methods = { }
sql.methods = methods
+local helpers = { }
+sql.helpers = helpers
+
local serialize = table.fastserialize
local deserialize = table.deserialize
sql.serialize = serialize
sql.deserialize = deserialize
+helpers.serialize = serialize -- bonus
+helpers.deserialize = deserialize -- bonus
+
local defaults = { __index =
{
resultfile = "result.dat",
@@ -122,92 +108,105 @@ local defaults = { __index =
},
}
--- Experiments with an p/action demonstrated that there is not much gain. We could do a runtime
--- capture but creating all the small tables is not faster and it doesn't work well anyway.
+table.setmetatableindex(sql.methods,function(t,k)
+ require("util-sql-imp-"..k)
+ return rawget(t,k)
+end)
-local separator = P("\t")
-local newline = patterns.newline
-local empty = Cc("")
+-- converters
-local entry = C((1-separator-newline)^0) -- C 10% faster than Cs
-
-local unescaped = P("\\n") / "\n"
- + P("\\t") / "\t"
- + P("\\\\") / "\\"
+local converters = { }
+sql.converters = converters
-local entry = Cs((unescaped + (1-separator-newline))^0) -- C 10% faster than Cs but Cs needed due to nesting
+local function makeconverter(entries,celltemplate,wraptemplate)
+ local shortcuts = { }
+ local assignments = { }
+ local function cell(i)
+ return format(celltemplate,i,i)
+ end
+ 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] = booleanstring(%s),",nam,cell(i))
+ elseif typ == "number" then
+ assignments[i] = format("[%q] = tonumber(%s),",nam,cell(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(%s),",nam,c,cell(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[%s],",nam,#converters,cell(i))
+ elseif typ == "deserialize" then
+ assignments[i] = format("[%q] = deserialize(%s),",nam,cell(i))
+ else
+ assignments[i] = format("[%q] = %s,",nam,cell(i))
+ end
+ end
+ local code = format(wraptemplate,concat(shortcuts,"\n"),concat(assignments,"\n "))
+ local func = loadstring(code)
+ return func and func()
+end
-local getfirst = Ct( entry * (separator * (entry+empty))^0) + newline
-local skipfirst = (1-newline)^1 * newline
-local getfirstline = C((1-newline)^0)
+function sql.makeconverter(entries)
+ local fields = { }
+ for i=1,#entries do
+ fields[i] = format("`%s`",entries[i].name)
+ end
+ fields = concat(fields, ", ")
+ local converter = {
+ fields = fields
+ }
+ table.setmetatableindex(converter, function(t,k)
+ local sqlmethod = methods[k]
+ local v = makeconverter(entries,sqlmethod.celltemplate,sqlmethod.wraptemplate)
+ t[k] = v
+ return v
+ end)
+ return converter, fields
+end
-local cache = { }
+-- helper for libraries:
-local function splitdata(data) -- todo: hash on first line
- if data == "" then
- if trace_sql then
- report_state("no data")
- end
- return { }, { }
- end
- local first = lpegmatch(getfirstline,data)
- if not first then
- if trace_sql then
- report_state("no data")
- end
- return { }, { }
+local function validspecification(specification)
+ local presets = specification.presets
+ if type(presets) == "string" then
+ presets = dofile(presets)
end
- local p = cache[first]
- if p then
- -- report_state("reusing: %s",first)
- local entries = lpegmatch(p.parser,data)
- return entries or { }, p.keys
- elseif p == false then
- return { }, { }
- elseif p == nil then
- local keys = lpegmatch(getfirst,first) or { }
- if #keys == 0 then
- if trace_sql then
- report_state("no banner")
- end
- cache[first] = false
- return { }, { }
- end
- -- quite generic, could be a helper
- local n = #keys
- if n == 0 then
- report_state("no fields")
- cache[first] = false
- return { }, { }
- end
- if n == 1 then
- local key = keys[1]
- if trace_sql then
- report_state("one field with name",key)
- end
- p = Cg(Cc(key) * entry)
- else
- for i=1,n do
- local key = keys[i]
- if trace_sql then
- report_state("field %s has name %q",i,key)
- end
- local s = Cg(Cc(key) * entry)
- if p then
- p = p * separator * s
- else
- p = s
- end
- end
- end
- p = Cf(Ct("") * p,rawset) * newline^1
- p = skipfirst * Ct(p^0)
- cache[first] = { parser = p, keys = keys }
- local entries = lpegmatch(p,data)
- return entries or { }, keys
+ if type(presets) == "table" then
+ setmetatable(presets,defaults)
+ setmetatable(specification,{ __index = presets })
+ else
+ setmetatable(specification,defaults)
end
+ return true
end
+helpers.validspecification = validspecification
+
+local whitespace = patterns.whitespace^0
+local eol = patterns.eol
+local separator = P(";")
+local escaped = patterns.escaped
+local dquote = patterns.dquote
+local squote = patterns.squote
+local dsquote = squote * squote
+---- quoted = patterns.quoted
+local quoted = dquote * (escaped + (1-dquote))^0 * dquote
+ + squote * (escaped + dsquote + (1-squote))^0 * squote
+local comment = P("--") * (1-eol) / ""
+local query = whitespace
+ * Cs((quoted + comment + 1 - separator)^1 * Cc(";"))
+ * whitespace
+local splitter = Ct(query * (separator * query)^0)
+
+helpers.querysplitter = splitter
+
-- I will add a bit more checking.
local function validspecification(specification)
@@ -263,411 +262,35 @@ local function preparetemplate(specification)
report_state("no query template or templatefile")
end
+helpers.preparetemplate = preparetemplate
-local function dataprepared(specification)
- local query = preparetemplate(specification)
- if query then
- io.savedata(specification.queryfile,query)
- os.remove(specification.resultfile)
- if trace_queries then
- report_state("query: %s",query)
- end
- return true
- else
- -- maybe push an error
- os.remove(specification.queryfile)
- os.remove(specification.resultfile)
- end
-end
-
-local function datafetched(specification,runner)
- local command = replacetemplate(runner,specification)
- if trace_sql then
- local t = osclock()
- report_state("command: %s",command)
- local okay = os.execute(command)
- report_state("fetchtime: %.3f sec",osclock()-t) -- not okay under linux
- return okay == 0
- else
- return os.execute(command) == 0
- end
-end
-
-local function dataloaded(specification)
- if trace_sql then
- local t = osclock()
- local data = io.loaddata(specification.resultfile) or ""
- report_state("datasize: %.3f MB",#data/1024/1024)
- report_state("loadtime: %.3f sec",osclock()-t)
- return data
- else
- return io.loaddata(specification.resultfile) or ""
- end
-end
-
-local function dataconverted(data)
- if trace_sql then
- local t = osclock()
- local data, keys = splitdata(data)
- report_state("converttime: %.3f",osclock()-t)
- report_state("keys: %s ",#keys)
- report_state("entries: %s ",#data)
- return data, keys
- else
- return splitdata(data)
- end
-end
-
-sql.splitdata = splitdata
-
--- todo: new, etc
-
-local function execute(specification)
- if trace_sql then
- report_state("executing client")
- end
- if not validspecification(specification) then
- report_state("error in specification")
- return
- end
- if not dataprepared(specification) then
- report_state("error in preparation")
- return
- end
- if not datafetched(specification,methods.client.runner) then
- report_state("error in fetching, query: %s",string.collapsespaces(io.loaddata(specification.queryfile)))
- return
- end
- local data = dataloaded(specification)
- if not data then
- report_state("error in loading")
- return
- end
- local data, keys = dataconverted(data)
- if not data then
- report_state("error in converting")
- return
- end
- local one = data[1]
- if one then
- setmetatable(data,{ __index = one } )
- end
- return data, keys
-end
-
-methods.client = {
- runner = [[mysql --batch --user="%username%" --password="%password%" --host="%host%" --port=%port% --database="%database%" < "%queryfile%" > "%resultfile%"]],
- execute = execute,
- serialize = serialize,
- deserialize = deserialize,
- usesfiles = true,
-}
-
-local function dataloaded(specification)
- if trace_sql then
- local t = osclock()
- local data = table.load(specification.resultfile)
- report_state("loadtime: %.3f sec",osclock()-t)
- return data
- else
- return table.load(specification.resultfile)
- end
-end
-
-local function dataconverted(data)
- if trace_sql then
- local data, keys = data.data, data.keys
- report_state("keys: %s ",#keys)
- report_state("entries: %s ",#data)
- return data, keys
- else
- return data.data, data.keys
- end
-end
-
-local function execute(specification)
- if trace_sql then
- report_state("executing lmxsql")
- end
- if not validspecification(specification) then
- report_state("error in specification")
- return
- end
- if not dataprepared(specification) then
- report_state("error in preparation")
- return
- end
- if not datafetched(specification,methods.lmxsql.runner) then
- report_state("error in fetching, query: %s",string.collapsespaces(io.loaddata(specification.queryfile)))
- return
- end
- local data = dataloaded(specification)
- if not data then
- report_state("error in loading")
- return
- end
- local data, keys = dataconverted(data)
- if not data then
- report_state("error in converting")
- return
- end
- local one = data[1]
- if one then
- setmetatable(data,{ __index = one } )
- end
- return data, keys
-end
-
-methods.lmxsql = {
- runner = [[lmx-sql %host% %port% "%username%" "%password%" "%database%" "%queryfile%" "%resultfile%"]],
- execute = execute,
- serialize = serialize,
- deserialize = deserialize,
- usesfiles = true,
-}
-
-local mysql = nil
-local cache = { }
-
-local function validspecification(specification)
- local presets = specification.presets
- if type(presets) == "string" then
- presets = dofile(presets)
- end
- if type(presets) == "table" then
- setmetatable(presets,defaults)
- setmetatable(specification,{ __index = presets })
- else
- setmetatable(specification,defaults)
- end
- return true
-end
-
-local dataprepared = preparetemplate
-
-local function connect(session,specification)
- return session:connect(
- specification.database or "",
- specification.username or "",
- specification.password or "",
- specification.host or "",
- specification.port
- )
-end
-
-local whitespace = patterns.whitespace^0
-local eol = patterns.eol
-local separator = P(";")
-local escaped = patterns.escaped
-local dquote = patterns.dquote
-local squote = patterns.squote
-local dsquote = squote * squote
----- quoted = patterns.quoted
-local quoted = dquote * (escaped + (1-dquote))^0 * dquote
- + squote * (escaped + dsquote + (1-squote))^0 * squote
-local comment = P("--") * (1-eol) / ""
-local query = whitespace
- * Cs((quoted + comment + 1 - separator)^1 * Cc(";"))
- * whitespace
-local splitter = Ct(query * (separator * query)^0)
+-- -- -- we delay setting this -- -- --
-local function datafetched(specification,query,converter)
- if not query or query == "" then
- report_state("no valid query")
- return { }, { }
- end
- local id = specification.id
- local session, connection
- if id then
- local c = cache[id]
- if c then
- session = c.session
- connection = c.connection
- end
- if not connection then
- session = mysql()
- connection = connect(session,specification)
- cache[id] = { session = session, connection = connection }
- end
- else
- session = mysql()
- 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, okay
- for i=1,#query do
- local q = query[i]
- 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 okay and id then
- if session then
- session:close()
- end
- if connection then
- connection:close()
- end
- session = mysql() -- maybe not needed
- connection = connect(session,specification)
- cache[id] = { session = session, connection = connection }
- for i=1,#query do
- local q = query[i]
- 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 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
- end
- end
- end
- result:close()
- elseif message then
- report_state("message %s",message)
- end
- if not keys then
- keys = { }
- end
- if not data then
- data = { }
- end
- if not id then
- connection:close()
- session:close()
- end
- return data, keys
-end
+local currentmethod
-local function execute(specification)
- if not mysql then
- local lib = require("luasql.mysql")
- if lib then
- mysql = lib.mysql
- else
- report_state("error in loading luasql.mysql")
- end
- end
- if trace_sql then
- report_state("executing library")
- end
- if not validspecification(specification) then
- report_state("error in specification")
- return
- end
- local query = dataprepared(specification)
- if not query then
- report_state("error in preparation")
- return
- end
- local data, keys = datafetched(specification,query,specification.converter)
- if not data then
- report_state("error in fetching")
- return
- end
- local one = data[1]
- if one then
- setmetatable(data,{ __index = one } )
- end
- return data, keys
+local function firstexecute(...)
+ local execute = methods[currentmethod].execute
+ sql.execute = execute
+ return execute(...)
end
-methods.library = {
- runner = function() end, -- never called
- execute = execute,
- serialize = serialize,
- deserialize = deserialize,
- usesfiles = false,
-}
-
--- -- --
-
-local currentmethod
-
function sql.setmethod(method)
- local m = methods[method]
- if m then
- currentmethod = method
- sql.execute = m.execute
- return m
- else
- return methods[currentmethod]
- end
+ currentmethod = method
+ sql.execute = firstexecute
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
+ 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)
+ local execute = nil
+ local m_execute = method.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)
@@ -756,74 +379,6 @@ sql.tokens = {
-- -- --
-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[i] = {
- %s
- }
- end
- return data
-end
-]]
-
-function sql.makeconverter(entries,deserialize)
- local shortcuts = { }
- local assignments = { }
- local fields = { }
- 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
- fields[#fields+1] = format("`%s`",nam)
- 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(), concat(fields, ", ")
- end
-end
-
-- local func, code = sql.makeconverter {
-- { name = "a", type = "number" },
-- { name = "b", type = "string" },
diff --git a/tex/context/base/x-xfdf.mkiv b/tex/context/base/x-xfdf.mkiv
index c4ca0c427..460220ed9 100644
--- a/tex/context/base/x-xfdf.mkiv
+++ b/tex/context/base/x-xfdf.mkiv
@@ -60,6 +60,7 @@
\def\xfdfload #1#2{\xmlloadonly{#1}{#2}{xfdf}}
\def\xfdfvalue#1#2{\xmlfirst{#1}{/xfdf/fields/field[@name='#2']/value}}
+\def\xfdftext #1#2{\xmlfirst{#1}{/xfdf/fields/field[@name='#2']/value/paragraphs()}}
% \startxmlsetups xfdf:b
% \bold{\xmlflush{#1}}
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index 9862853db..844ff18cf 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/25/12 21:44:34
+-- merge date : 10/02/12 15:13:09
do -- begin closure to overcome local limits and interference
@@ -2033,30 +2033,49 @@ function boolean.tonumber(b)
end
function toboolean(str,tolerant)
- if str == true or str == false then
- return str
- elseif tolerant then
- local tstr = type(str)
- if tstr == "string" then
- return str == "true" or str == "yes" or str == "on" or str == "1" or str == "t"
- elseif tstr == "number" then
- return tonumber(str) ~= 0
- elseif tstr == "nil" then
- return false
- else
- return str
- end
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
elseif str == "true" then
return true
elseif str == "false" then
return false
+ elseif not tolerant then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
else
- return str
+ return str == "yes" or str == "on" or str == "t"
end
end
string.toboolean = toboolean
+function string.booleanstring(str)
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
+ elseif str == "true" then
+ return true
+ elseif str == "false" then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
+ else
+ return str == "yes" or str == "on" or str == "t"
+ end
+end
+
function string.is_boolean(str,default)
if type(str) == "string" then
if str == "true" or str == "yes" or str == "on" or str == "t" then