From df4e78545a9a7e02cca523a2912aa1684f6867c6 Mon Sep 17 00:00:00 2001
From: Hans Hagen <pragma@wxs.nl>
Date: Tue, 2 Oct 2012 15:13:00 +0200
Subject: beta 2012.10.02 15:13

---
 scripts/context/lua/mtxrun.lua                     |  99 ++-
 scripts/context/stubs/mswin/mtxrun.lua             |  99 ++-
 scripts/context/stubs/unix/mtxrun                  |  99 ++-
 tex/context/base/char-utf.lua                      |   8 +
 tex/context/base/cont-new.mkii                     |   2 +-
 tex/context/base/cont-new.mkiv                     |   2 +-
 tex/context/base/context-version.pdf               | Bin 4147 -> 4146 bytes
 tex/context/base/context-version.png               | Bin 106662 -> 104882 bytes
 tex/context/base/context.mkii                      |   2 +-
 tex/context/base/context.mkiv                      |   2 +-
 tex/context/base/l-boolean.lua                     |  47 +-
 tex/context/base/l-unicode.lua                     |  26 +-
 tex/context/base/lxml-lpt.lua                      |  26 +
 tex/context/base/math-ini.mkiv                     |   8 +-
 tex/context/base/status-files.pdf                  | Bin 24572 -> 24499 bytes
 tex/context/base/status-lua.pdf                    | Bin 195090 -> 195100 bytes
 tex/context/base/strc-num.lua                      |  13 +-
 tex/context/base/strc-sec.mkiv                     |   1 +
 tex/context/base/syst-aux.mkiv                     |  24 +-
 tex/context/base/util-sql-imp-client.lua           | 253 ++++++++
 tex/context/base/util-sql-imp-library.lua          | 282 ++++++++
 tex/context/base/util-sql-imp-swiglib.lua          | 419 ++++++++++++
 tex/context/base/util-sql-users.lua                |  96 ++-
 tex/context/base/util-sql.lua                      | 721 ++++-----------------
 tex/context/base/x-xfdf.mkiv                       |   1 +
 tex/generic/context/luatex/luatex-fonts-merged.lua |  49 +-
 26 files changed, 1547 insertions(+), 732 deletions(-)
 create mode 100644 tex/context/base/util-sql-imp-client.lua
 create mode 100644 tex/context/base/util-sql-imp-library.lua
 create mode 100644 tex/context/base/util-sql-imp-swiglib.lua

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 100755
--- 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
Binary files a/tex/context/base/context-version.pdf and b/tex/context/base/context-version.pdf differ
diff --git a/tex/context/base/context-version.png b/tex/context/base/context-version.png
index 91f5d5658..33ac9f216 100644
Binary files a/tex/context/base/context-version.png and b/tex/context/base/context-version.png 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
Binary files a/tex/context/base/status-files.pdf and b/tex/context/base/status-files.pdf differ
diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf
index 1e5074cb6..9507ef95c 100644
Binary files a/tex/context/base/status-lua.pdf and b/tex/context/base/status-lua.pdf 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
-- 
cgit v1.2.3