From 55bcf0e607bb8af553581e74293687ed635bf877 Mon Sep 17 00:00:00 2001
From: Hans Hagen 
Date: Mon, 24 Mar 2008 23:24:00 +0100
Subject: stable 2008.03.24 23:24
---
 metapost/context/base/mp-spec.mp   |  42 ++++----
 metapost/context/base/mp-tool.mp   |  16 ++-
 scripts/context/lua/luatools.lua   |  88 ++++++++++++----
 scripts/context/lua/mtx-update.lua |   1 +
 scripts/context/lua/mtxrun.lua     | 201 ++++++++++++++++++++++++++-----------
 scripts/context/ruby/base/tex.rb   |   4 +-
 tex/context/base/attr-ini.lua      |  10 ++
 tex/context/base/attr-ini.tex      |   4 +
 tex/context/base/char-def.lua      |  16 +++
 tex/context/base/char-utf.lua      |  40 +++++---
 tex/context/base/colo-new.lua      | 192 +++++++++++++++++++----------------
 tex/context/base/colo-new.mkiv     |  12 +--
 tex/context/base/colo-new.tex      |  25 -----
 tex/context/base/core-inc.lua      |  15 +--
 tex/context/base/core-inc.mkiv     |  16 ++-
 tex/context/base/core-job.lua      |   5 +-
 tex/context/base/l-dir.lua         | 108 +++++++++++++-------
 tex/context/base/l-xml.lua         |  12 ++-
 tex/context/base/luat-tex.lua      |   2 +-
 tex/context/base/luat-tmp.lua      |   7 +-
 tex/context/base/lxml-ini.tex      |  57 +++++++++++
 tex/context/base/math-ini.lua      |  83 ++++++++-------
 tex/context/base/meta-pdf.mkiv     |   2 +-
 tex/context/base/s-sys-01.tex      |  42 ++++++++
 tex/context/base/x-mmp.mkiv        |  11 +-
 tex/context/interface/keys-ro.xml  |   2 +-
 26 files changed, 681 insertions(+), 332 deletions(-)
 create mode 100644 tex/context/base/s-sys-01.tex
diff --git a/metapost/context/base/mp-spec.mp b/metapost/context/base/mp-spec.mp
index d4c2b8cfc..351f9fe1c 100644
--- a/metapost/context/base/mp-spec.mp
+++ b/metapost/context/base/mp-spec.mp
@@ -330,33 +330,26 @@ def set_linear_vector (suffix a,b)(expr p,n) =
   fi ;
 enddef ;
 
+def set_circular_vector (suffix ab, r)(expr p,n) =
+  if     (n=1) : ab := llcorner p ;
+  elseif (n=2) : ab := lrcorner p ;
+  elseif (n=3) : ab := urcorner p ;
+  elseif (n=4) : ab := ulcorner p ;
+  else         : ab := center   p ; r := .5r ;
+  fi ;
+enddef ;
+
 def linear_shade (expr p, n, ca, cb) =
   begingroup ;
   save a, b, sh ; pair a, b ;
   set_linear_vector(a,b)(p,n) ;
   fill p withshade define_linear_shade (a,b,ca,cb) ;
   if trace_shades :
-    drawarrow a -- b withpen pencircle scaled 1pt ;
+    drawarrow a -- b withpen pencircle scaled 1pt withcolor .5white ;
   fi ;
   endgroup ;
 enddef ;
 
-vardef predefined_linear_shade (expr p, n, ca, cb) =
-  save a, b, sh ; pair a, b ;
-  set_linear_vector(a,b)(p,n) ;
-  set_shade_vector(a,b)(p,n) ;
-  define_linear_shade (a,b,ca,cb)
-enddef ;
-
-def set_circular_vector (suffix ab, r)(expr p,n) =
-  if     (n=1) : ab := llcorner p ;
-  elseif (n=2) : ab := lrcorner p ;
-  elseif (n=3) : ab := urcorner p ;
-  elseif (n=4) : ab := ulcorner p ;
-  else         : ab := center   p ; r := .5r ;
-  fi ;
-enddef ;
-
 def circular_shade (expr p, n, ca, cb) =
   begingroup ;
   save ab, r ; pair ab ; numeric r ;
@@ -365,11 +358,18 @@ def circular_shade (expr p, n, ca, cb) =
   set_circular_vector(ab,r)(p,n) ;
   fill p withshade define_circular_shade(ab,ab,0,r,ca,cb) ;
   if trace_shades :
-    drawarrow ab -- ab shifted (0,r) withpen pencircle scaled 1pt ;
+    drawarrow ab -- ab shifted (0,r) withpen pencircle scaled 1pt withcolor .5white ;
   fi ;
   endgroup ;
 enddef ;
 
+vardef predefined_linear_shade (expr p, n, ca, cb) =
+  save a, b, sh ; pair a, b ;
+  set_linear_vector(a,b)(p,n) ;
+  set_shade_vector(a,b)(p,n) ;
+  define_linear_shade (a,b,ca,cb)
+enddef ;
+
 vardef predefined_circular_shade (expr p, n, ca, cb) =
   save ab, r ; pair ab ; numeric r ;
   r := (xpart lrcorner p - xpart llcorner p) ++
@@ -512,9 +512,9 @@ enddef ;
 
 % newcolor truecyan, truemagenta, trueyellow ;
 %
-% truecyan    = cmyk (1,0,0,0) ;
-% truemagenta = cmyk (0,1,0,0) ;
-% trueyellow  = cmyk (0,0,1,0) ;
+% truecyan    = (1,0,0,0) ;
+% truemagenta = (0,1,0,0) ;
+% trueyellow  = (0,0,1,0) ;
 
 %D Spot colors
 
diff --git a/metapost/context/base/mp-tool.mp b/metapost/context/base/mp-tool.mp
index 567ff16fd..b8e2dd668 100644
--- a/metapost/context/base/mp-tool.mp
+++ b/metapost/context/base/mp-tool.mp
@@ -116,7 +116,21 @@ extra_endfig := extra_endfig
 %D Also handy (when we flush colors):
 
 vardef dddecimal primary c =
-  decimal redpart c & " " & decimal greenpart c & " " & decimal bluepart  c
+  decimal redpart c & " " & decimal greenpart c & " " & decimal bluepart c
+enddef ;
+
+vardef ddddecimal primary c =
+  decimal cyanpart c & " " & decimal magentapart c & " " & decimal yellowpart c & " " & decimal blackpart c
+enddef ;
+
+vardef colordecimals primary c =
+    if cmykcolor c :
+        decimal cyanpart c & ":" & decimal magentapart c & ":" & decimal yellowpart c & ":" & decimal blackpart c
+    elseif rgbcolor c :
+        decimal redpart c & ":" & decimal greenpart c & ":" & decimal bluepart c
+    else :
+        decimal c
+    fi
 enddef ;
 
 %D We have standardized data file names:
diff --git a/scripts/context/lua/luatools.lua b/scripts/context/lua/luatools.lua
index 80291325c..2d146149e 100644
--- a/scripts/context/lua/luatools.lua
+++ b/scripts/context/lua/luatools.lua
@@ -1098,6 +1098,24 @@ function table.hexed(t,seperator)
     return table.concat(tt,seperator or " ")
 end
 
+function table.reverse_hash(h)
+    local r = { }
+    for k,v in pairs(h) do
+        r[v] = (k:gsub(" ","")):lower()
+    end
+    return r
+end
+
+function table.reverse(t)
+    local tt = { }
+    if #t > 0 then
+        for i=#t,1,-1 do
+            tt[#tt+1] = t[i]
+        end
+    end
+    return tt
+end
+
 
 -- filename : l-io.lua
 -- comment  : split off from luat-lib
@@ -1835,6 +1853,7 @@ if lfs then do
         if ok and type(scanner) == "function" then
             if not path:find("/$") then path = path .. '/' end
             for name in scanner do
+print(name)
                 local full = path .. name
                 local mode = attributes(full,'mode')
                 if mode == 'file' then
@@ -1950,23 +1969,28 @@ if lfs then do
             end
             local first, middle, last
             local drive = false
-            first, last = str:match("^(//)/*(.-)$")
+            first, middle, last = str:match("^(//)(//*)(.*)$")
             if first then
-                middle, last = str:match("([^/]+)/+(.-)$")
-                if middle then
-                    pth = "//" .. middle
-                else
-                    pth = "//" .. last
-                    last = ""
-                end
+                -- empty network path == local path
             else
-                first, middle, last = str:match("^([a-zA-Z]:)(/*)(.-)$")
+                first, last = str:match("^(//)/*(.-)$")
                 if first then
-                    pth, drive = first .. middle, true
+                    middle, last = str:match("([^/]+)/+(.-)$")
+                    if middle then
+                        pth = "//" .. middle
+                    else
+                        pth = "//" .. last
+                        last = ""
+                    end
                 else
-                    middle, last = str:match("^(/*)(.-)$")
-                    if not middle then
-                        last = str
+                    first, middle, last = str:match("^([a-zA-Z]:)(/*)(.-)$")
+                    if first then
+                        pth, drive = first .. middle, true
+                    else
+                        middle, last = str:match("^(/*)(.-)$")
+                        if not middle then
+                            last = str
+                        end
                     end
                 end
             end
@@ -1998,17 +2022,38 @@ if lfs then do
 --~         print(dir.mkdirs("a/bbb//ccc/"))
 
         function dir.expand_name(str)
-            local first, last = str:match("^(//)/*(.*)$")
+            local first, nothing, last = str:match("^(//)(//*)(.*)$")
+            if first then
+                first = lfs.currentdir() .. "/"
+                first = first:gsub("\\","/")
+            end
+            if not first then
+                first, last = str:match("^(//)/*(.*)$")
+            end
             if not first then
                 first, last = str:match("^([a-zA-Z]:)(.*)$")
+                if first and not last:find("^/") then
+                    local d = lfs.currentdir()
+                    if lfs.chdir(first) then
+                        first = lfs.currentdir()
+                        first = first:gsub("\\","/")
+                    end
+                    lfs.chdir(d)
+                end
             end
             if not first then
-                first, last = lfs.currentdir() .. "/", str
+                first, last = lfs.currentdir(), str
                 first = first:gsub("\\","/")
             end
             last = last:gsub("//","/")
             last = last:gsub("/%./","/")
-            return first .. last
+            last = last:gsub("^/*","")
+            first = first:gsub("/*$","")
+            if last == "" then
+                return first
+            else
+                return first .. "/" .. last
+            end
         end
 
     else
@@ -4129,7 +4174,7 @@ function input.aux.find_file(instance,filename) -- todo : plugin (scanners, chec
             end
             -- this is actually 'other text files' or 'any' or 'whatever'
             local filelist = input.aux.collect_files(instance,wantedfiles)
-            local lf = filelist and filelist[1]
+            local fl = filelist and filelist[1]
             if fl then
                 filename = fl[3]
                 result[#result+1] = filename
@@ -4890,9 +4935,10 @@ end
 
 -- here we use the cache for format loading (texconfig.[formatname|jobname])
 
-if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
-    if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end
-    texconfig.formatname = caches.setpath(instance,"format") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
+--~ if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
+if tex and texconfig and (not texconfig.formatname or texconfig.formatname == "") and texmf.instance then
+    if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end -- or luc
+    texconfig.formatname = caches.setpath(texmf.instance,"formats") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
 end
 
 --[[ldx--
@@ -5673,7 +5719,7 @@ if texconfig and not texlua then do
             ws("graphics processing time  - %s seconds (n=%s) (including tex)", input.loadtime(figures), figures.n or "?")
         end
         if metapost then
-            ws("metapost processing time  - %s seconds (+ loading: %s seconds)", input.loadtime(metapost), input.loadtime(mplib))
+            ws("metapost processing time  - %s seconds (loading: %s seconds, execution: %s seconds, n: %s)", input.loadtime(metapost), input.loadtime(mplib), input.loadtime(metapost.exectime), metapost.n)
         end
         if status.luastate_bytes then
             ws("current memory usage      - %s bytes", status.luastate_bytes)
diff --git a/scripts/context/lua/mtx-update.lua b/scripts/context/lua/mtx-update.lua
index d2630f95c..4908db4da 100644
--- a/scripts/context/lua/mtx-update.lua
+++ b/scripts/context/lua/mtx-update.lua
@@ -127,6 +127,7 @@ scripts.update.platforms = {
     ["win32"]     = "mswin",
     ["win"]       = "mswin",
     ["linux"]     = "linux",
+    ["freebsd"]   = "freebsd",
     ["linux-32"]  = "linux",
     ["linux-64"]  = "linux-64",
     ["osx"]       = "osx-intel",
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index 9a9a68d38..aa78a553e 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -1112,6 +1112,24 @@ function table.hexed(t,seperator)
     return table.concat(tt,seperator or " ")
 end
 
+function table.reverse_hash(h)
+    local r = { }
+    for k,v in pairs(h) do
+        r[v] = (k:gsub(" ","")):lower()
+    end
+    return r
+end
+
+function table.reverse(t)
+    local tt = { }
+    if #t > 0 then
+        for i=#t,1,-1 do
+            tt[#tt+1] = t[i]
+        end
+    end
+    return tt
+end
+
 
 -- filename : l-io.lua
 -- comment  : split off from luat-lib
@@ -1736,18 +1754,68 @@ dir = { }
 
 if lfs then do
 
+--~     local attributes = lfs.attributes
+--~     local walkdir    = lfs.dir
+--~
+--~     local function glob_pattern(path,patt,recurse,action)
+--~         local ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+--~         if ok and type(scanner) == "function" then
+--~             if not path:find("/$") then path = path .. '/' end
+--~             for name in scanner do
+--~                 local full = path .. name
+--~                 local mode = attributes(full,'mode')
+--~                 if mode == 'file' then
+--~                     if name:find(patt) then
+--~                         action(full)
+--~                     end
+--~                 elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+--~                     glob_pattern(full,patt,recurse,action)
+--~                 end
+--~             end
+--~         end
+--~     end
+--~
+--~     dir.glob_pattern = glob_pattern
+--~
+--~     local function glob(pattern, action)
+--~         local t = { }
+--~         local action = action or function(name) t[#t+1] = name end
+--~         local path, patt = pattern:match("^(.*)/*%*%*/*(.-)$")
+--~         local recurse = path and patt
+--~         if not recurse then
+--~             path, patt = pattern:match("^(.*)/(.-)$")
+--~             if not (path and patt) then
+--~                 path, patt = '.', pattern
+--~             end
+--~         end
+--~         patt = patt:gsub("([%.%-%+])", "%%%1")
+--~         patt = patt:gsub("%*", ".*")
+--~         patt = patt:gsub("%?", ".")
+--~         patt = "^" .. patt .. "$"
+--~      -- print('path: ' .. path .. ' | pattern: ' .. patt .. ' | recurse: ' .. tostring(recurse))
+--~         glob_pattern(path,patt,recurse,action)
+--~         return t
+--~     end
+--~
+--~     dir.glob = glob
+
     local attributes = lfs.attributes
     local walkdir    = lfs.dir
 
     local function glob_pattern(path,patt,recurse,action)
-        local ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+        local ok, scanner
+        if path == "/" then
+            ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
+        else
+            ok, scanner = xpcall(function() return walkdir(path)      end, function() end) -- kepler safe
+        end
         if ok and type(scanner) == "function" then
             if not path:find("/$") then path = path .. '/' end
             for name in scanner do
                 local full = path .. name
                 local mode = attributes(full,'mode')
                 if mode == 'file' then
-                    if name:find(patt) then
+                    if full:find(patt) then
                         action(full)
                     end
                 elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
@@ -1761,29 +1829,36 @@ if lfs then do
 
     local function glob(pattern, action)
         local t = { }
-        local action = action or function(name) table.insert(t,name) end
-        local path, patt = pattern:match("^(.*)/*%*%*/*(.-)$")
-        local recurse = path and patt
-        if not recurse then
-            path, patt = pattern:match("^(.*)/(.-)$")
-            if not (path and patt) then
-                path, patt = '.', pattern
-            end
-        end
-        patt = patt:gsub("([%.%-%+])", "%%%1")
-        patt = patt:gsub("%*", ".*")
-        patt = patt:gsub("%?", ".")
-        patt = "^" .. patt .. "$"
-     -- print('path: ' .. path .. ' | pattern: ' .. patt .. ' | recurse: ' .. tostring(recurse))
+        local path, rest, patt, recurse
+        local action = action or function(name) t[#t+1] = name end
+        local pattern = pattern:gsub("^%*%*","./**")
+        local pattern = pattern:gsub("/%*/","/**/")
+        path, rest = pattern:match("^(/)(.-)$")
+        if path then
+            path = path
+        else
+            path, rest = pattern:match("^([^/]*)/(.-)$")
+        end
+        patt = rest:gsub("([%.%-%+])", "%%%1")
+        patt = patt:gsub("%*", "[^/]*")
+        patt = patt:gsub("%?", "[^/]")
+        patt = patt:gsub("%[%^/%]%*%[%^/%]%*", ".*")
+        if path == "" then path = "." end
+        -- print(pattern, path, patt)
+        recurse = patt:find("%.%*/")
         glob_pattern(path,patt,recurse,action)
         return t
     end
 
     dir.glob = glob
 
-    -- todo: speedup
+    --~ list = dir.glob("**/*.tif")
+    --~ list = dir.glob("/**/*.tif")
+    --~ list = dir.glob("./**/*.tif")
+    --~ list = dir.glob("oeps/**/*.tif")
+    --~ list = dir.glob("/oeps/**/*.tif")
 
-    local function globfiles(path,recurse,func,files)
+    local function globfiles(path,recurse,func,files) -- func == pattern or function
         if type(func) == "string" then
             local s = func -- alas, we need this indirect way
             func = function(name) return name:find(s) end
@@ -1825,23 +1900,6 @@ if lfs then do
     --~ mkdirs(".","/a/b/c")
     --~ mkdirs("a","b","c")
 
---~     function dir.mkdirs(...)
---~         local pth, err, lst = "", false, table.concat({...},"/")
---~         for _, s in ipairs(lst:split("/")) do
---~             if pth == "" then
---~                 pth = (s == "" and "/") or s
---~             else
---~                 pth = pth .. "/" .. s
---~             end
---~             if s == "" then
---~                 -- can be network path
---~             elseif not lfs.isdir(pth) then
---~                 lfs.mkdir(pth)
---~             end
---~         end
---~         return pth, not err
---~     end
-
     local make_indeed = true -- false
 
     if string.find(os.getenv("PATH"),";") then
@@ -1859,23 +1917,28 @@ if lfs then do
             end
             local first, middle, last
             local drive = false
-            first, last = str:match("^(//)/*(.-)$")
+            first, middle, last = str:match("^(//)(//*)(.*)$")
             if first then
-                middle, last = str:match("([^/]+)/+(.-)$")
-                if middle then
-                    pth = "//" .. middle
-                else
-                    pth = "//" .. last
-                    last = ""
-                end
+                -- empty network path == local path
             else
-                first, middle, last = str:match("^([a-zA-Z]:)(/*)(.-)$")
+                first, last = str:match("^(//)/*(.-)$")
                 if first then
-                    pth, drive = first .. middle, true
+                    middle, last = str:match("([^/]+)/+(.-)$")
+                    if middle then
+                        pth = "//" .. middle
+                    else
+                        pth = "//" .. last
+                        last = ""
+                    end
                 else
-                    middle, last = str:match("^(/*)(.-)$")
-                    if not middle then
-                        last = str
+                    first, middle, last = str:match("^([a-zA-Z]:)(/*)(.-)$")
+                    if first then
+                        pth, drive = first .. middle, true
+                    else
+                        middle, last = str:match("^(/*)(.-)$")
+                        if not middle then
+                            last = str
+                        end
                     end
                 end
             end
@@ -1907,17 +1970,38 @@ if lfs then do
 --~         print(dir.mkdirs("a/bbb//ccc/"))
 
         function dir.expand_name(str)
-            local first, last = str:match("^(//)/*(.*)$")
+            local first, nothing, last = str:match("^(//)(//*)(.*)$")
+            if first then
+                first = lfs.currentdir() .. "/"
+                first = first:gsub("\\","/")
+            end
+            if not first then
+                first, last = str:match("^(//)/*(.*)$")
+            end
             if not first then
                 first, last = str:match("^([a-zA-Z]:)(.*)$")
+                if first and not last:find("^/") then
+                    local d = lfs.currentdir()
+                    if lfs.chdir(first) then
+                        first = lfs.currentdir()
+                        first = first:gsub("\\","/")
+                    end
+                    lfs.chdir(d)
+                end
             end
             if not first then
-                first, last = lfs.currentdir() .. "/", str
+                first, last = lfs.currentdir(), str
                 first = first:gsub("\\","/")
             end
             last = last:gsub("//","/")
             last = last:gsub("/%./","/")
-            return first .. last
+            last = last:gsub("^/*","")
+            first = first:gsub("/*$","")
+            if last == "" then
+                return first
+            else
+                return first .. "/" .. last
+            end
         end
 
     else
@@ -3072,7 +3156,7 @@ do
                 end
             else
                 if (command == 16 or command == 12) and index == 1 then -- initial
---~                     wildcard = true
+                --  wildcard = true
                     wildcard = command == 16 -- ok?
                     index = index + 1
                     action = pattern[index]
@@ -3178,8 +3262,8 @@ do
                                 if matched then -- combine tg test and at test
                                     if index == #pattern then
                                         if handle(root,rootdt,root.ri or k) then return false end
-                                        if wildcard and multiple then
---~ if wildcard or multiple then
+--~                                         if wildcard and multiple then
+if wildcard or multiple then
                                             if not traverse(e,pattern,handle,reverse,index,root,true) then return false end
                                         end
                                     else
@@ -5842,7 +5926,7 @@ function input.aux.find_file(instance,filename) -- todo : plugin (scanners, chec
             end
             -- this is actually 'other text files' or 'any' or 'whatever'
             local filelist = input.aux.collect_files(instance,wantedfiles)
-            local lf = filelist and filelist[1]
+            local fl = filelist and filelist[1]
             if fl then
                 filename = fl[3]
                 result[#result+1] = filename
@@ -6603,9 +6687,10 @@ end
 
 -- here we use the cache for format loading (texconfig.[formatname|jobname])
 
-if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
-    if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end
-    texconfig.formatname = caches.setpath(instance,"format") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
+--~ if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
+if tex and texconfig and (not texconfig.formatname or texconfig.formatname == "") and texmf.instance then
+    if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end -- or luc
+    texconfig.formatname = caches.setpath(texmf.instance,"formats") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
 end
 
 --[[ldx--
diff --git a/scripts/context/ruby/base/tex.rb b/scripts/context/ruby/base/tex.rb
index 02683bd60..bf2f92547 100644
--- a/scripts/context/ruby/base/tex.rb
+++ b/scripts/context/ruby/base/tex.rb
@@ -577,7 +577,7 @@ class TEX
         if data = (IO.readlines(@@luafiles) rescue nil) then
             report("compiling lua files (using #{File.expand_path(@@luafiles)})")
             begin
-                Dir.makedirs(@@luatarget) rescue false
+                File.makedirs(@@luatarget) rescue false
                 data.each do |line|
                     luafile = line.chomp
                     lucfile = File.basename(luafile).gsub(/\..*?$/,'') + ".luc"
@@ -2057,7 +2057,7 @@ end
         setvariable('mp.line','')
         setvariable('mp.error','')
         if mpdata = File.silentread(mpname) then
-            mpdata.gsub!(/^\#.*\n/o,'')
+            mpdata.gsub!(/^\%.*\n/o,'')
             File.silentrename(mpname,mpcopy)
             texfound = mergebe || (mpdata =~ /btex .*? etex/mo)
             if mp = openedfile(mpname) then
diff --git a/tex/context/base/attr-ini.lua b/tex/context/base/attr-ini.lua
index c701740e3..7b379151a 100644
--- a/tex/context/base/attr-ini.lua
+++ b/tex/context/base/attr-ini.lua
@@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['attr-ini'] = {
     license   = "see context related readme files"
 }
 
+-- todo: document this
+
 -- nb: attributes: color etc is much slower than normal (marks + literals) but ...
 -- nb. too many "0 g"s
 
@@ -551,6 +553,8 @@ colors.models = {
     cmyk = 4,
 }
 
+colors.model = "all"
+
 do
 
     local min    = math.min
@@ -597,6 +601,11 @@ do
         return rgbtogray(cmyktorgb(c,m,y,k))
     end
 
+    colors.rgbtocmyk  = rgbtocmyk
+    colors.rgbtogray  = rgbtogray
+    colors.cmyktorgb  = cmyktorgb
+    colors.cmyktogray = cmyktogray
+
     -- we can share some *data by using s, rgb and cmyk hashes, but
     -- normally the amount of colors is not that large; storing the
     -- components costs a bit of extra runtime, but we expect to gain
@@ -663,6 +672,7 @@ do
 end
 
 function colors.setmodel(attribute,name)
+    colors.model = name
     colors.selector = attributes.numbers[attribute]
     colors.default = colors.models[name] or 1
     return colors.default
diff --git a/tex/context/base/attr-ini.tex b/tex/context/base/attr-ini.tex
index f7d01b1fd..5cd49818c 100644
--- a/tex/context/base/attr-ini.tex
+++ b/tex/context/base/attr-ini.tex
@@ -96,6 +96,10 @@
 
 \setcolormodel{all}
 
+\appendtoks
+    \setcolormodel{all}% redundant?
+\to \everyjob
+
 \def\registerrgbcolor   #1#2#3#4{\ctxlua{colors.register('color','#1','rgb' ,#2,#3,#4)}}
 \def\registercmykcolor#1#2#3#4#5{\ctxlua{colors.register('color','#1','cmyk',#2,#3,#4,#5)}}
 \def\registergraycolor      #1#2{\ctxlua{colors.register('color','#1','gray',#2)}}
diff --git a/tex/context/base/char-def.lua b/tex/context/base/char-def.lua
index 4f55dbed8..59f8ed4ac 100644
--- a/tex/context/base/char-def.lua
+++ b/tex/context/base/char-def.lua
@@ -42799,6 +42799,8 @@ characters.data={
   ["category"]="po",
   ["cjkwd"]="a",
   ["contextname"]="textbullet",
+  ["mathclass"]="binary", -- ok?
+  ["mathname"] = "bullet",
   ["description"]="BULLET",
   ["linebreak"]="al",
   ["unicodeslot"]=0x2022, -- •
@@ -44877,6 +44879,8 @@ characters.data={
   ["cjkwd"]="a",
   ["description"]="LEFTWARDS ARROW",
   ["linebreak"]="ai",
+  ["mathclass"]="relation",
+  ["mathname"]="leftarrow",
   ["unicodeslot"]=0x2190, -- ←
  },
  [0x2191]={
@@ -44893,6 +44897,8 @@ characters.data={
   ["cjkwd"]="a",
   ["description"]="RIGHTWARDS ARROW",
   ["linebreak"]="ai",
+  ["mathclass"]="relation",
+  ["mathname"]="rightarrow",
   ["unicodeslot"]=0x2192, -- →
  },
  [0x2193]={
@@ -44909,6 +44915,8 @@ characters.data={
   ["cjkwd"]="a",
   ["description"]="LEFT RIGHT ARROW",
   ["linebreak"]="ai",
+  ["mathclass"]="relation",
+  ["mathname"]="leftrightarrow",
   ["unicodeslot"]=0x2194, -- ↔
  },
  [0x2195]={
@@ -45286,6 +45294,8 @@ characters.data={
   ["category"]="so",
   ["description"]="LEFTWARDS DOUBLE ARROW",
   ["linebreak"]="al",
+  ["mathclass"]="relation",
+  ["mathname"]="Leftarrow",
   ["unicodeslot"]=0x21D0, -- ⇐
  },
  [0x21D1]={
@@ -45301,6 +45311,8 @@ characters.data={
   ["cjkwd"]="a",
   ["description"]="RIGHTWARDS DOUBLE ARROW",
   ["linebreak"]="ai",
+  ["mathclass"]="relation",
+  ["mathname"]="Rightarrow",
   ["unicodeslot"]=0x21D2, -- ⇒
  },
  [0x21D3]={
@@ -45316,6 +45328,8 @@ characters.data={
   ["cjkwd"]="a",
   ["description"]="LEFT RIGHT DOUBLE ARROW",
   ["linebreak"]="ai",
+  ["mathclass"]="relation",
+  ["mathname"]="Leftrightarrow",
   ["unicodeslot"]=0x21D4, -- ⇔
  },
  [0x21D5]={
@@ -51538,6 +51552,8 @@ characters.data={
   ["cjkwd"]="a",
   ["description"]="WHITE UP-POINTING TRIANGLE",
   ["linebreak"]="ai",
+  ["mathclass"]="ord",
+  ["mathname"]="triangle",
   ["unicodeslot"]=0x25B3, -- △
  },
  [0x25B4]={
diff --git a/tex/context/base/char-utf.lua b/tex/context/base/char-utf.lua
index fcb8804dd..5e5d575c5 100644
--- a/tex/context/base/char-utf.lua
+++ b/tex/context/base/char-utf.lua
@@ -138,34 +138,42 @@ to their right glyph there.
 0x100000.
 --ldx]]--
 
-characters.filters.utf.private      = { }
-characters.filters.utf.private.high = { }
-characters.filters.utf.private.low  = { }
+characters.filters.utf.private = {
+    high    = { },
+    low     = { },
+    escapes = { },
+}
 
 do
 
+    local low     = characters.filters.utf.private.low
+    local high    = characters.filters.utf.private.high
+    local escapes = characters.filters.utf.private.escapes
+    local special = "~#$%^&_{}\\"
+
     local ub, uc, ug = utf.byte, utf.char, utf.gsub
-    local cfup = characters.filters.utf.private
 
     function characters.filters.utf.private.set(ch)
-        local cb = ub(ch)
+        local cb
+        if type(ch) == "number" then
+            cb, ch = ch, uc(ch)
+        else
+            cb = ub(ch)
+        end
         if cb < 256 then
-            cfup.low[ch] = uc(0x0F0000 + cb)
-            cfup.high[uc(0x0F0000 + cb)] = ch
+            low    [ch]                = uc(0x0F0000 + cb)
+            high   [uc(0x0F0000 + cb)] = ch
+            escapes[ch]                = "\\" .. ch
         end
     end
 
-    function characters.filters.utf.private.replace(str)
-        ug("(.)", cfup.low)
-    end
+    function characters.filters.utf.private.replace(str) return ug(str,"(.)", low    ) end
+    function characters.filters.utf.private.revert(str)  return ug(str,"(.)", high   ) end
+    function characters.filters.utf.private.escape(str)  return ug(str,"(.)", escapes) end
 
-    function characters.filters.utf.private.revert(str)
-        ug("(.)", cfup.high)
-    end
+    local set = characters.filters.utf.private.set
 
-    for _, ch in ipairs({ '~', '#', '$', '%', '^', '&', '_', '{', '}' }) do
-        cfup.set(ch)
-    end
+    for ch in special:gmatch(".") do set(ch) end
 
 end
 
diff --git a/tex/context/base/colo-new.lua b/tex/context/base/colo-new.lua
index 0a689e050..d039e47c7 100644
--- a/tex/context/base/colo-new.lua
+++ b/tex/context/base/colo-new.lua
@@ -16,6 +16,8 @@ if not modules then modules = { } end modules ['colo-ini'] = {
 
 -- spec-pdf.lua
 
+-- todo: %s -> %f
+
 backends     = backends     or { }
 backends.pdf = backends.pdf or { }
 backend      = backends.pdf
@@ -55,6 +57,8 @@ ctx.aux = ctx.aux or { }
 
 do
 
+    local format, sprint = string.format, tex.sprint
+
     local a_l_c_template = "\\setevalue{(ca:%s)}{%s}" ..
                            "\\setevalue{(cs:%s)}{\\dosetattribute{color}{%s}}"
     local a_g_c_template = "\\setxvalue{(ca:%s)}{%s}" ..
@@ -84,68 +88,64 @@ do
     function ctx.aux.definecolor(name, ca, global)
         if ca and ca > 0 then
             if global then
-                tex.sprint(tex.ctxcatcodes,a_g_c_template:format(name, ca, name, ca))
+                sprint(tex.ctxcatcodes,a_g_c_template:format(name, ca, name, ca))
             else
-                tex.sprint(tex.ctxcatcodes,a_l_c_template:format(name, ca, name, ca))
+                sprint(tex.ctxcatcodes,a_l_c_template:format(name, ca, name, ca))
             end
         else
             if global then
-                tex.sprint(tex.ctxcatcodes,r_g_c_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_g_c_template:format(name, name))
             else
-                tex.sprint(tex.ctxcatcodes,r_l_c_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_l_c_template:format(name, name))
             end
         end
     end
     function ctx.aux.inheritcolor(name, ca, global)
         if ca and ca ~= "" then
             if global then
-                tex.sprint(tex.ctxcatcodes,f_g_c_template:format(name, ca, name, ca))
+                sprint(tex.ctxcatcodes,f_g_c_template:format(name, ca, name, ca))
             else
-                tex.sprint(tex.ctxcatcodes,f_l_c_template:format(name, ca, name, ca))
+                sprint(tex.ctxcatcodes,f_l_c_template:format(name, ca, name, ca))
             end
         else
             if global then
-                tex.sprint(tex.ctxcatcodes,r_g_c_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_g_c_template:format(name, name))
             else
-                tex.sprint(tex.ctxcatcodes,r_l_c_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_l_c_template:format(name, name))
             end
         end
     end
     function ctx.aux.definetransparent(name, ta, global)
         if ta and ta > 0 then
             if global then
-                tex.sprint(tex.ctxcatcodes,a_g_t_template:format(name, ta, name, ta))
+                sprint(tex.ctxcatcodes,a_g_t_template:format(name, ta, name, ta))
             else
-                tex.sprint(tex.ctxcatcodes,a_l_t_template:format(name, ta, name, ta))
+                sprint(tex.ctxcatcodes,a_l_t_template:format(name, ta, name, ta))
             end
         else
             if global then
-                tex.sprint(tex.ctxcatcodes,r_g_t_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_g_t_template:format(name, name))
             else
-                tex.sprint(tex.ctxcatcodes,r_l_t_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_l_t_template:format(name, name))
             end
         end
     end
     function ctx.aux.inherittransparent(name, ta, global)
         if ta and ta ~= "" then
             if global then
-                tex.sprint(tex.ctxcatcodes,f_g_t_template:format(name, ta, name, ta))
+                sprint(tex.ctxcatcodes,f_g_t_template:format(name, ta, name, ta))
             else
-                tex.sprint(tex.ctxcatcodes,f_l_t_template:format(name, ta, name, ta))
+                sprint(tex.ctxcatcodes,f_l_t_template:format(name, ta, name, ta))
             end
         else
             if global then
-                tex.sprint(tex.ctxcatcodes,r_g_t_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_g_t_template:format(name, name))
             else
-                tex.sprint(tex.ctxcatcodes,r_l_t_template:format(name, name))
+                sprint(tex.ctxcatcodes,r_l_t_template:format(name, name))
             end
         end
     end
 
-end
-
-do
-
     local transparent = {
         none       =  0,
         normal     =  1,
@@ -172,7 +172,10 @@ do
         transparent[name] = n
     end
 
+    local registered = { }
+
     local function registerspotcolor(parent,name,parentnumber,e,f,d,p)
+if not registered[parentnumber] then
         local v = colors.values[parentnumber]
         if v then
             local kind = v[1]
@@ -185,9 +188,12 @@ do
             end
             backends.pdf.registerspotcolorname(parent,e)
         end
+    registered[parentnumber] = true
+end
     end
 
     local function registermultitonecolor(parent,name,parentnumber,e,f,d,p) -- same as spot but different template
+if not registered[parentnumber] then
         local v = colors.values[parentnumber]
         if v then
             local kind = v[1]
@@ -199,13 +205,15 @@ do
                 backend.registercmykindexcolor(parent,f,d,p,v[6],v[7],v[8],v[9])
             end
         end
+    registered[parentnumber] = true
+end
     end
 
     function ctx.definesimplegray(name,s)
         return colors.register('color',name,'gray',s) -- we still need to get rid of 'color'
     end
 
-    function ctx.defineprocesscolor(prefix,name,str,global,freeze) -- still inconsistent color vs transparent
+    function ctx.defineprocesscolor(name,str,global,freeze) -- still inconsistent color vs transparent
         local t = str:split_settings()
         if t then
             if t.h then
@@ -239,40 +247,56 @@ do
         end
     end
 
-    function ctx.definespotcolor(prefix,name,parent,str,global)
-        if name ~= parent then
+    function ctx.definespotcolor(name,parent,str,global)
+        if parent == "" or parent:find("=") then
+            ctx.registerspotcolor(name, parent)
+        elseif name ~= parent then
             local cp = attributes.list[attributes.numbers['color']][parent]
             if cp then
                 local t = str:split_settings()
                 if t then
                     t.p = tonumber(t.p) or 1
                     registerspotcolor(parent, name, cp, t.e, 1, "", t.p) -- p not really needed, only diagnostics
-                    ctx.aux.definecolor(name, colors.register('color',name,'spot', parent, 1, "", t.p), true)
-                    if t.a and t.t then
-                        ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global)
-                    elseif ctx.couplecolors then
---~                         ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up
-                        ctx.aux.definetransparent(name, 0, global) -- can be sped up
+                    if name and name ~= "" then
+                        ctx.aux.definecolor(name, colors.register('color',name,'spot', parent, 1, "", t.p), true)
+                        if t.a and t.t then
+                            ctx.aux.definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global)
+                        elseif ctx.couplecolors then
+                        --~ ctx.aux.definetransparent(name, transparencies.register(nil, 1, 1), global) -- can be sped up
+                            ctx.aux.definetransparent(name, 0, global) -- can be sped up
+                        end
                     end
                 end
             end
         end
     end
 
-    function ctx.definemultitonecolor(prefix,name,multispec,colorspec,selfspec)
+    function ctx.registerspotcolor(parent, str)
+        local cp = attributes.list[attributes.numbers['color']][parent]
+        if cp then
+            local e = ""
+            if str then
+                local t = str:split_settings()
+                e = (t and t.e) or ""
+            end
+            registerspotcolor(parent, "dummy", cp, e, 1, "", 1) -- p not really needed, only diagnostics
+        end
+    end
+
+    function ctx.definemultitonecolor(name,multispec,colorspec,selfspec)
         local dd, pp, nn = { }, { }, { }
         for k,v in multispec:gmatch("(%a+)=([^%,]*)") do
             dd[#dd+1] = k
             pp[#pp+1] = v
             nn[#nn+1] = k
-            nn[#nn+1] = string.format("%1.3g",tonumber(v))
+            nn[#nn+1] = format("%1.3g",tonumber(v))
         end
     --~ v = tonumber(v) * p
         local nof = #dd
         if nof > 0 then
             dd, pp, nn = table.concat(dd,','), table.concat(pp,','), table.concat(nn,'_')
             local parent = (nn:lower()):gsub("[^%d%a%.]+","_")
-            ctx.defineprocesscolor(prefix,parent,colorspec..","..selfspec,true,true)
+            ctx.defineprocesscolor(parent,colorspec..","..selfspec,true,true)
             local cp = attributes.list[attributes.numbers['color']][parent]
             if cp then
                 registerspotcolor     (parent, name, cp, "", nof, dd, pp)
@@ -298,28 +322,28 @@ do
             end
             if tv then
                 if model == 2 then
-                    return string.format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5])
+                    return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5])
                 elseif model == 3 then
-                    return string.format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5])
+                    return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5])
                 elseif model == 4 then
-                    return string.format("transparent(%s,%s,cmyk(%s,%s,%s,%s))",tv[1],tv[2],cv[6],cv[7],cv[8],cv[9])
+                    return format("transparent(%s,%s,cmyk(%s,%s,%s,%s))",tv[1],tv[2],cv[6],cv[7],cv[8],cv[9])
                 else
-                    return string.format("transparent(%s,%s,multitonecolor(\"%s\",%s,\"%s\",\"%s\"))",tv[1],tv[2],cv[10],cv[11],cv[12],cv[13])
+                    return format("transparent(%s,%s,multitonecolor(\"%s\",%s,\"%s\",\"%s\"))",tv[1],tv[2],cv[10],cv[11],cv[12],cv[13])
                 end
             else
                 if model == 2 then
-                    return string.format("(%s,%s,%s)",cv[3],cv[4],cv[5])
+                    return format("(%s,%s,%s)",cv[3],cv[4],cv[5])
                 elseif model == 3 then
-                    return string.format("(%s,%s,%s)",cv[3],cv[4],cv[5])
+                    return format("(%s,%s,%s)",cv[3],cv[4],cv[5])
                 elseif model == 4 then
-                    return string.format("cmyk(%s,%s,%s,%s)",cv[6],cv[7],cv[8],cv[9])
+                    return format("cmyk(%s,%s,%s,%s)",cv[6],cv[7],cv[8],cv[9])
                 else
-                    return string.format("multitonecolor(\"%s\",%s,\"%s\",\"%s\")",cv[10],cv[11],cv[12],cv[13])
+                    return format("multitonecolor(\"%s\",%s,\"%s\",\"%s\")",cv[10],cv[11],cv[12],cv[13])
                 end
             end
         else
             default = default or 0 -- rgb !
-            return string.format("(%s,%s,%s)",default,default,default)
+            return format("(%s,%s,%s)",default,default,default)
         end
     end
 
@@ -355,15 +379,15 @@ do
         if cv then
             local model = cv[1]
             if model == 2 then
-                return string.format("s=%1.3f",cv[2])
+                return format("s=%1.3f",cv[2])
             elseif model == 3 then
-                return string.format("r=%1.3f g=%1.3f b=%1.3f",cv[3],cv[4],cv[5])
+                return format("r=%1.3f g=%1.3f b=%1.3f",cv[3],cv[4],cv[5])
             elseif model == 4 then
-                return string.format("c=%1.3f m=%1.3f y=%1.3f k=%1.3f",cv[6],cv[7],cv[8],cv[9])
+                return format("c=%1.3f m=%1.3f y=%1.3f k=%1.3f",cv[6],cv[7],cv[8],cv[9])
             elseif type(cv[13]) == "string" then
-                return string.format("p=%s",cv[13])
+                return format("p=%s",cv[13])
             else
-                return string.format("p=%1.3f",cv[13])
+                return format("p=%1.3f",cv[13])
             end
         else
             return ""
@@ -373,7 +397,7 @@ do
     function ctx.transparencycomponents(ta)
         local tv = transparencies.value(ta)
         if tv then
-            return string.format("a=%1.3f t=%1.3f",tv[1],tv[2])
+            return format("a=%1.3f t=%1.3f",tv[1],tv[2])
         else
             return ""
         end
@@ -387,22 +411,22 @@ do
             end
             if model == 2 then
                 local s = cv[2]
-                return string.format("%s g %s G",s,s)
+                return format("%s g %s G",s,s)
             elseif model == 3 then
                 local r, g, b = cv[3], cv[4], cv[5]
-                return string.format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b)
+                return format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b)
             elseif model == 4 then
                 local c, m, y, k = cv[6],cv[7],cv[8],cv[9]
-                return string.format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k)
+                return format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k)
             else
                 local n,f,d,p = cv[10],cv[11],cv[12],cv[13]
                 if type(p) == "string" then
                     p = p:gsub(","," ") -- brr misuse of spot
                 end
-                return string.format("/%s cs /%s CS %s SCN %s scn",n,n,p,p)
+                return format("/%s cs /%s CS %s SCN %s scn",n,n,p,p)
             end
         else
-            return string.format("%s g %s G",default or 0)
+            return format("%s g %s G",default or 0)
         end
     end
 
@@ -413,16 +437,16 @@ do
                 model = cv[1]
             end
             if model == 2 then
-                return string.format("%s",cv[2])
+                return format("%s",cv[2])
             elseif model == 3 then
-                return string.format("%s %s %s",cv[3],cv[4],cv[5])
+                return format("%s %s %s",cv[3],cv[4],cv[5])
             elseif model == 4 then
-                return string.format("%s %s %s %s",cv[6],cv[7],cv[8],cv[9])
+                return format("%s %s %s %s",cv[6],cv[7],cv[8],cv[9])
             else
-                return string.format("%s",cv[13])
+                return format("%s",cv[13])
             end
         else
-            return string.format("%s",default or 0)
+            return format("%s",default or 0)
         end
     end
 
@@ -433,16 +457,16 @@ do
                 model = cv[1]
             end
             if model == 2 then
-                return string.format("[%s]",cv[2])
+                return format("[%s]",cv[2])
             elseif model == 3 then
-                return string.format("[%s %s %s]",cv[3],cv[4],cv[5])
+                return format("[%s %s %s]",cv[3],cv[4],cv[5])
             elseif model == 4 then
-                return string.format("[%s %s %s %s]",cv[6],cv[7],cv[8],cv[9])
+                return format("[%s %s %s %s]",cv[6],cv[7],cv[8],cv[9])
             elseif model == 4 then
-                return string.format("[%s]",cv[13])
+                return format("[%s]",cv[13])
             end
         else
-            return string.format("[%s]",default or 0)
+            return format("[%s]",default or 0)
         end
     end
 
@@ -479,33 +503,33 @@ do
         return tostring(v)
     end
 
-end
+    -- unfortunately we have \cs's here but this will go anyway once we have mplib and such
 
--- unfortunately we have \cs's here but this will go anyway once we have mplib and such
+    function ctx.resolvempgraycolor(csa,csb,model,s)
+        local ca = colors.register('color',nil,'gray',s)
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
+    end
+    function ctx.resolvemprgbcolor(csa,csb,model,r,g,b)
+        local ca = colors.register('color',nil,'rgb',r,g,b)
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
+    end
+    function ctx.resolvempcmykcolor(csa,csb,model,c,m,y,k)
+        local ca = colors.register('color',nil,'cmyk',c,m,y,k)
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
+    end
+    function ctx.resolvempspotcolor(csa,csb,model,n,f,d,p)
+        local ca = colors.register('color',nil,'spot',n,f,d,p)
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
+        tex.sprint(tex.ctxcatcodes,format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
+    end
 
-function ctx.resolvempgraycolor(csa,csb,model,s)
-    local ca = colors.register('color',nil,'gray',s)
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
-end
-function ctx.resolvemprgbcolor(csa,csb,model,r,g,b)
-    local ca = colors.register('color',nil,'rgb',r,g,b)
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
-end
-function ctx.resolvempcmykcolor(csa,csb,model,c,m,y,k)
-    local ca = colors.register('color',nil,'cmyk',c,m,y,k)
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
-end
-function ctx.resolvempspotcolor(csa,csb,model,n,f,d,p)
-    local ca = colors.register('color',nil,'spot',n,f,d,p)
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csa,ctx.pdfcolorvalue(model,ca)))
-    tex.sprint(tex.ctxcatcodes,string.format("\\setxvalue{%s}{%s}",csb,ctx.pdfcolorspace(model,ca)))
 end
 
 -- literals needed to inject code in the mp stream, we cannot use attributes there
--- since literals may have qQ's
+-- since literals may have qQ's, much may go away once we have mplib code in place
 
 do
 
diff --git a/tex/context/base/colo-new.mkiv b/tex/context/base/colo-new.mkiv
index 296ddad3a..d7cf33c51 100644
--- a/tex/context/base/colo-new.mkiv
+++ b/tex/context/base/colo-new.mkiv
@@ -127,31 +127,29 @@
 \def\dodefinecolorcommand#1#2%
   {\unexpanded#1{#2}{\doactivatecolor{#2}}}
 
-\let\colordefinitionprefix\empty
-
 \def\dodefinecolor[#1][#2]%
   {\addtocommalist{#1}\colorlist
-   \ctxlua{ctx.defineprocesscolor("\colordefinitionprefix","#1","#2",false,\iffreezecolors true\else false\fi)}%
+   \ctxlua{ctx.defineprocesscolor("#1","#2",false,\iffreezecolors true\else false\fi)}%
    \dodefinecolorcommand\setvalue{#1}}
 
 \def\dodefineglobalcolor[#1][#2]%
   {\doglobal\addtocommalist{#1}\colorlist
-   \ctxlua{ctx.defineprocesscolor("\colordefinitionprefix","#1","#2",true,\iffreezecolors true\else false\fi)}%
+   \ctxlua{ctx.defineprocesscolor("#1","#2",true,\iffreezecolors true\else false\fi)}%
    \dodefinecolorcommand\setgvalue{#1}}
 
 \def\dodefinenamedcolor[#1][#2]%
   {\doglobal\addtocommalist{#1}\colorlist
-   \ctxlua{ctx.defineprocesscolor("\colordefinitionprefix","#1","#2",false,\iffreezecolors true\else false\fi)}%
+   \ctxlua{ctx.defineprocesscolor("#1","#2",false,\iffreezecolors true\else false\fi)}%
    \dodefinecolorcommand\setvalue{#1}}
 
 \def\dodefinespotcolor[#1][#2][#3]%
   {\doglobal\addtocommalist{#1}\colorlist % optional
    \doglobal\addtocommalist{#2}\allspotcolors
-   \ctxlua{ctx.definespotcolor("\colordefinitionprefix","#1","#2","#3",true)}%
+   \ctxlua{ctx.definespotcolor("#1","#2","#3",true)}%
    \dodefinecolorcommand\setxvalue{#1}}
 
 \def\dodefinemultitonecolor[#1][#2][#3][#4]%
-  {\ctxlua{ctx.definemultitonecolor("\colordefinitionprefix","#1","#2","#3","#4",true)}%
+  {\ctxlua{ctx.definemultitonecolor("#1","#2","#3","#4",true)}%
    \dodefinecolorcommand\setxvalue{#1}}
 
 \def\dodefinetransparency[#1][#2]%
diff --git a/tex/context/base/colo-new.tex b/tex/context/base/colo-new.tex
index 06e53f086..417c54a52 100644
--- a/tex/context/base/colo-new.tex
+++ b/tex/context/base/colo-new.tex
@@ -741,31 +741,6 @@
 \def\definecolorgroup
   {\dotripleempty\dodefinecolorgroup}
 
-% \def\dodefinecolorgroup[#1][#2][#3]%
-%   {\ifthirdargument
-%      \processaction
-%        [#2]
-%        [    \v!cmyk=>\edef\currentcolorspace{C},
-%              \v!rgb=>\edef\currentcolorspace{R},
-%             \v!gray=>\edef\currentcolorspace{S},
-%             \v!spot=>\edef\currentcolorspace{P},
-%                \v!s=>\edef\currentcolorspace{S},
-%          \s!unknown=>\edef\currentcolorspace{R}]%
-%      \colorcount\zerocount
-%      \def\dododefinecolorgroup##1%
-%        {\advance\colorcount \plusone
-%         \setevalue{\??cr#1:\the\colorcount}{\currentcolorspace:##1:0:0}}%
-%      \processcommalist[#3]\dododefinecolorgroup
-%    \else
-%      \doifinstringelse{:}{#2}
-%        {\definecolorgroup[#1][\v!rgb][#2]}
-%        {\doloop
-%           {\doifdefinedelse{\??cr#2:\recurselevel}
-%              {\setevalue{\??cr#1:\recurselevel}%
-%                 {\csname\??cr#2:\recurselevel\endcsname}}
-%              {\exitloop}}}%
-%    \fi}
-
 \def\dododefinecolorgroupgray         [#1][#2:#3]{\definecolor   [#1:\the\colorcount][s=#2]}
 \def\dododefinecolorgrouprgb    [#1][#2:#3:#4:#5]{\definecolor   [#1:\the\colorcount][r=#2,g=#3,b=#4]}
 \def\dododefinecolorgroupcmyk[#1][#2:#3:#4:#5:#6]{\definecolor   [#1:\the\colorcount][c=#2,m=#3=,y=#4,k=#5]}
diff --git a/tex/context/base/core-inc.lua b/tex/context/base/core-inc.lua
index c86078cdd..e81b28abd 100644
--- a/tex/context/base/core-inc.lua
+++ b/tex/context/base/core-inc.lua
@@ -294,7 +294,7 @@ do
         local figuredata = figures.new()
         if request then
             local iv = interfaces.variables
-            local w, h = tonumber(request.width), tonumber(request.heiht)
+            local w, h = tonumber(request.width), tonumber(request.height)
             request.page      = math.max(tonumber(request.page) or 1,1)
             request.size      = img.check_size(request.size)
             request.object    = iv[request.object] == "yes"
@@ -596,11 +596,11 @@ function figures.existers.generic(askedname)
 end
 function figures.checkers.generic(data)
     local dr, du, ds = data.request, data.used, data.status
-    local name, page = du.fullname or "unknown generic", dr.page
-    local hash = name .. "->" .. page
+    local name, page, size = du.fullname or "unknown generic", dr.page, dr.size or "crop"
+    local hash = name .. "->" .. page .. "->" .. size
     local figure = figures.loaded[hash]
     if figure == nil then
-        figure = img.new { filename = name, page = page }
+        figure = img.new { filename = name, page = page, pagebox = dr.size }
         figure = (figure and img.scan(figure)) or false
         figures.loaded[hash] = figure
     end
@@ -628,8 +628,11 @@ function figures.includers.generic(data)
         figures.used[hash] = figure
     end
     if figure then
-        tex.box[figures.boxnumber] = img.node(figure) -- img.write(figure)
+        local n = figures.boxnumber
+        tex.box[n] = img.node(figure) -- img.write(figure)
+        tex.wd[n], tex.ht[n], tex.dp[n] = figure.width, figure.height, 0 -- new, hm, tricky, we need to do that in tex (yet)
         ds.objectnumber = figure.objnum
+        tex.sprint(tex.ctxcatcodes,"\\relocateexternalfigure")
     end
     return data
 end
@@ -766,7 +769,7 @@ function figures.bases.find(basename,askedlabel)
         end
         t = false
         if base[2] and base[3] then
-            for e, d, k in xml.elements(base[3],"/(rlx:library|figurelibrary)/*:figure/*:label") do
+            for e, d, k in xml.elements(base[3],"/(*:library|figurelibrary)/*:figure/*:label") do
                 page = page + 1
                 if xml.content(d[k]) == askedlabel then
                     t = {
diff --git a/tex/context/base/core-inc.mkiv b/tex/context/base/core-inc.mkiv
index ff4ab3e6b..2d6075247 100644
--- a/tex/context/base/core-inc.mkiv
+++ b/tex/context/base/core-inc.mkiv
@@ -180,7 +180,7 @@
    \doifseparatingcolorselse
      {\let\@@efforegroundcolor\empty
       \doifelsenothing\@@efsplit
-        {\chardef\splitexternalfigure\zerocount}
+        {\charde\splitexternalfigure\zerocount}
         {\doifcolorchannelelse\@@efsplit
            {\let\@@efobject\v!no  % why?
             \chardef\splitexternalfigure\plusone}
@@ -263,6 +263,16 @@
    \fi
    \ctxlua{figures.pop()}}
 
+\def\relocateexternalfigure % easier here than in lua
+  {\global\setbox\foundexternalfigure\vbox to \ht\foundexternalfigure\bgroup
+     \vss
+     \ht\foundexternalfigure\zeropoint
+     \hbox to \wd\foundexternalfigure\bgroup
+        \box\foundexternalfigure
+        \hss
+     \egroup
+   \egroup}
+
 \def\signalexternalfigure
   {\ifcase\figurestatus
      \resetsystemmode\v!figure % todo, also: \v!resource
@@ -367,7 +377,7 @@
 \def\dogetfiguredimensions[#1][#2]%
   {\startnointerference
      \testexternalfigureonly
-     \externalfigure[#1][#2,\c!display=,\c!object=\v!no]
+     \externalfigure[#1][#2,\c!display=,\c!object=\v!no]%
    \stopnointerference}
 
 \let\getfiguredimensionsonly\getfiguredimensions
@@ -388,7 +398,7 @@
      \testexternalfigureonly
      \setfalse\externalfigureflush % == test ?
      \externalfigure[#1][#2][#3]% or \doexternalfigure
-     \externalfigure[#1][#2,\c!display=,\c!object=\v!no]
+     \externalfigure[#1][#2,\c!display=,\c!object=\v!no]%
    \stopnointerference}
 
 % figurebases
diff --git a/tex/context/base/core-job.lua b/tex/context/base/core-job.lua
index 8454d3d51..2493719e8 100644
--- a/tex/context/base/core-job.lua
+++ b/tex/context/base/core-job.lua
@@ -64,7 +64,8 @@ end
 function commands.processfile(name,maxreadlevel)
     name = find_file(name,maxreadlevel)
     if name ~= "" then
-        tex.sprint(tex.ctxcatcodes,string.format("\\input %s\\relax",name))
+    --  tex.sprint(tex.ctxcatcodes,string.format("\\input %s\\relax",name))
+        tex.print(tex.ctxcatcodes,string.format("\\input %s",name))
     end
 end
 
@@ -125,7 +126,7 @@ local function convertexamodes(str)
     end
 end
 
--- we need a system file option: ,. .. etc + path sbut no tex lookup so input.find_file is wrong here
+-- we need a system file option: ,. .. etc + paths but no tex lookup so input.find_file is wrong here
 
 function commands.loadexamodes(filename)
     if not filename or filename == "" then
diff --git a/tex/context/base/l-dir.lua b/tex/context/base/l-dir.lua
index 864e207b6..0cc913d13 100644
--- a/tex/context/base/l-dir.lua
+++ b/tex/context/base/l-dir.lua
@@ -12,18 +12,68 @@ dir = { }
 
 if lfs then do
 
+--~     local attributes = lfs.attributes
+--~     local walkdir    = lfs.dir
+--~
+--~     local function glob_pattern(path,patt,recurse,action)
+--~         local ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+--~         if ok and type(scanner) == "function" then
+--~             if not path:find("/$") then path = path .. '/' end
+--~             for name in scanner do
+--~                 local full = path .. name
+--~                 local mode = attributes(full,'mode')
+--~                 if mode == 'file' then
+--~                     if name:find(patt) then
+--~                         action(full)
+--~                     end
+--~                 elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+--~                     glob_pattern(full,patt,recurse,action)
+--~                 end
+--~             end
+--~         end
+--~     end
+--~
+--~     dir.glob_pattern = glob_pattern
+--~
+--~     local function glob(pattern, action)
+--~         local t = { }
+--~         local action = action or function(name) t[#t+1] = name end
+--~         local path, patt = pattern:match("^(.*)/*%*%*/*(.-)$")
+--~         local recurse = path and patt
+--~         if not recurse then
+--~             path, patt = pattern:match("^(.*)/(.-)$")
+--~             if not (path and patt) then
+--~                 path, patt = '.', pattern
+--~             end
+--~         end
+--~         patt = patt:gsub("([%.%-%+])", "%%%1")
+--~         patt = patt:gsub("%*", ".*")
+--~         patt = patt:gsub("%?", ".")
+--~         patt = "^" .. patt .. "$"
+--~      -- print('path: ' .. path .. ' | pattern: ' .. patt .. ' | recurse: ' .. tostring(recurse))
+--~         glob_pattern(path,patt,recurse,action)
+--~         return t
+--~     end
+--~
+--~     dir.glob = glob
+
     local attributes = lfs.attributes
     local walkdir    = lfs.dir
 
     local function glob_pattern(path,patt,recurse,action)
-        local ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+        local ok, scanner
+        if path == "/" then
+            ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
+        else
+            ok, scanner = xpcall(function() return walkdir(path)      end, function() end) -- kepler safe
+        end
         if ok and type(scanner) == "function" then
             if not path:find("/$") then path = path .. '/' end
             for name in scanner do
                 local full = path .. name
                 local mode = attributes(full,'mode')
                 if mode == 'file' then
-                    if name:find(patt) then
+                    if full:find(patt) then
                         action(full)
                     end
                 elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
@@ -37,29 +87,36 @@ if lfs then do
 
     local function glob(pattern, action)
         local t = { }
-        local action = action or function(name) table.insert(t,name) end
-        local path, patt = pattern:match("^(.*)/*%*%*/*(.-)$")
-        local recurse = path and patt
-        if not recurse then
-            path, patt = pattern:match("^(.*)/(.-)$")
-            if not (path and patt) then
-                path, patt = '.', pattern
-            end
+        local path, rest, patt, recurse
+        local action = action or function(name) t[#t+1] = name end
+        local pattern = pattern:gsub("^%*%*","./**")
+        local pattern = pattern:gsub("/%*/","/**/")
+        path, rest = pattern:match("^(/)(.-)$")
+        if path then
+            path = path
+        else
+            path, rest = pattern:match("^([^/]*)/(.-)$")
         end
-        patt = patt:gsub("([%.%-%+])", "%%%1")
-        patt = patt:gsub("%*", ".*")
-        patt = patt:gsub("%?", ".")
-        patt = "^" .. patt .. "$"
-     -- print('path: ' .. path .. ' | pattern: ' .. patt .. ' | recurse: ' .. tostring(recurse))
+        patt = rest:gsub("([%.%-%+])", "%%%1")
+        patt = patt:gsub("%*", "[^/]*")
+        patt = patt:gsub("%?", "[^/]")
+        patt = patt:gsub("%[%^/%]%*%[%^/%]%*", ".*")
+        if path == "" then path = "." end
+        -- print(pattern, path, patt)
+        recurse = patt:find("%.%*/")
         glob_pattern(path,patt,recurse,action)
         return t
     end
 
     dir.glob = glob
 
-    -- todo: speedup
+    --~ list = dir.glob("**/*.tif")
+    --~ list = dir.glob("/**/*.tif")
+    --~ list = dir.glob("./**/*.tif")
+    --~ list = dir.glob("oeps/**/*.tif")
+    --~ list = dir.glob("/oeps/**/*.tif")
 
-    local function globfiles(path,recurse,func,files)
+    local function globfiles(path,recurse,func,files) -- func == pattern or function
         if type(func) == "string" then
             local s = func -- alas, we need this indirect way
             func = function(name) return name:find(s) end
@@ -101,23 +158,6 @@ if lfs then do
     --~ mkdirs(".","/a/b/c")
     --~ mkdirs("a","b","c")
 
---~     function dir.mkdirs(...)
---~         local pth, err, lst = "", false, table.concat({...},"/")
---~         for _, s in ipairs(lst:split("/")) do
---~             if pth == "" then
---~                 pth = (s == "" and "/") or s
---~             else
---~                 pth = pth .. "/" .. s
---~             end
---~             if s == "" then
---~                 -- can be network path
---~             elseif not lfs.isdir(pth) then
---~                 lfs.mkdir(pth)
---~             end
---~         end
---~         return pth, not err
---~     end
-
     local make_indeed = true -- false
 
     if string.find(os.getenv("PATH"),";") then
diff --git a/tex/context/base/l-xml.lua b/tex/context/base/l-xml.lua
index 2ece2d91a..703a880ed 100644
--- a/tex/context/base/l-xml.lua
+++ b/tex/context/base/l-xml.lua
@@ -1033,7 +1033,7 @@ do
                 end
             else
                 if (command == 16 or command == 12) and index == 1 then -- initial
---~                     wildcard = true
+                --  wildcard = true
                     wildcard = command == 16 -- ok?
                     index = index + 1
                     action = pattern[index]
@@ -1139,9 +1139,13 @@ do
                                 if matched then -- combine tg test and at test
                                     if index == #pattern then
                                         if handle(root,rootdt,root.ri or k) then return false end
-                                        if wildcard and multiple then
---~ if wildcard or multiple then
-                                            if not traverse(e,pattern,handle,reverse,index,root,true) then return false end
+                                        if wildcard then
+                                            if multiple then
+                                                if not traverse(e,pattern,handle,reverse,index,root,true) then return false end
+                                            else
+                                             -- maybe or multiple; anyhow, check on (section|title) vs just section and title in example in lxml
+                                                if not traverse(e,pattern,handle,reverse,index,root) then return false end
+                                            end
                                         end
                                     else
                                         if not traverse(e,pattern,handle,reverse,index+1,root) then return false end
diff --git a/tex/context/base/luat-tex.lua b/tex/context/base/luat-tex.lua
index 74bf45938..1081af517 100644
--- a/tex/context/base/luat-tex.lua
+++ b/tex/context/base/luat-tex.lua
@@ -228,7 +228,7 @@ if texconfig and not texlua then do
             ws("graphics processing time  - %s seconds (n=%s) (including tex)", input.loadtime(figures), figures.n or "?")
         end
         if metapost then
-            ws("metapost processing time  - %s seconds (+ loading: %s seconds)", input.loadtime(metapost), input.loadtime(mplib))
+            ws("metapost processing time  - %s seconds (loading: %s seconds, execution: %s seconds, n: %s)", input.loadtime(metapost), input.loadtime(mplib), input.loadtime(metapost.exectime), metapost.n)
         end
         if status.luastate_bytes then
             ws("current memory usage      - %s bytes", status.luastate_bytes)
diff --git a/tex/context/base/luat-tmp.lua b/tex/context/base/luat-tmp.lua
index 59ceb260f..b4583133f 100644
--- a/tex/context/base/luat-tmp.lua
+++ b/tex/context/base/luat-tmp.lua
@@ -142,9 +142,10 @@ end
 
 -- here we use the cache for format loading (texconfig.[formatname|jobname])
 
-if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
-    if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end
-    texconfig.formatname = caches.setpath(instance,"format") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
+--~ if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
+if tex and texconfig and (not texconfig.formatname or texconfig.formatname == "") and texmf.instance then
+    if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end -- or luc
+    texconfig.formatname = caches.setpath(texmf.instance,"formats") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
 end
 
 --[[ldx--
diff --git a/tex/context/base/lxml-ini.tex b/tex/context/base/lxml-ini.tex
index a1a6cd905..750e242fc 100644
--- a/tex/context/base/lxml-ini.tex
+++ b/tex/context/base/lxml-ini.tex
@@ -215,3 +215,60 @@
 
 % \def\xmljoin{main}{/subject/information/authors/author}{,}{and}
 % table.join(t,1,#t-1,", ") .. " and " .. t[#t]
+
+% % An example by Wolfgang Schuster:
+%
+% \startxmlsetups xml:mysetups
+%   \xmlsetsetup{\xmldocument}{section{xml:*}
+%   \xmlsetsetup{\xmldocument}{title|p}{xml:*}
+% \stopxmlsetups
+%
+% \xmlregistersetup{xml:mysetups}
+%
+% \newcounter\sectionlevel
+%
+% \startxmlsetups xml:section
+%   \increment\sectionlevel
+%   \xmlflush{#1}
+%   \decrement\sectionlevel
+% \stopxmlsetups
+%
+% \startxmlsetups xml:title
+%   \ifcase\sectionlevel
+%   \or\chapter      {\xmlflush{#1}}
+%   \or\section      {\xmlflush{#1}}
+%   \or\subsection   {\xmlflush{#1}}
+%   \or\subsubsection{\xmlflush{#1}}
+%   \fi
+% \stopxmlsetups
+%
+% \startxmlsetups xml:p
+%   \xmlflush{#1}\endgraf
+% \stopxmlsetups
+%
+% \startbuffer[section]
+% 
+% 
+%     Section 1
+%     text text text text text text text text
+%     
+%         Subsection 1
+%         text text text text text text text text
+%     
+%     
+%         Subsection 2
+%         text text text text text text text text
+%     
+%     
+%         Subsection 3
+%         text text text text text text text text
+%     
+%     Section 2
+%     text text text text text text text text
+% 
+% \stopbuffer
+%
+% \starttext
+%     \xmlprocessbuffer{main}{section}{}
+% \stoptext
+
diff --git a/tex/context/base/math-ini.lua b/tex/context/base/math-ini.lua
index cfe28a09f..b060095cf 100644
--- a/tex/context/base/math-ini.lua
+++ b/tex/context/base/math-ini.lua
@@ -162,45 +162,43 @@ function mathematics.define()
                     end
                     mathematics.setmathcharacter(k,m,f,i,fe,ie)
                 end
-            else
-                if v.contextname then
-                    local s = slots[k]
-                    local c = v.contextname
-                    if s then
-                        local f, i, fe, ie = s[1], s[2], s[3], s[4]
-                        if mathematics.trace then
-                            trace(k,c,f,i,fe,ie)
-                        end
-                        -- todo: mathortext
-                        -- mathematics.setmathsymbol(c,m,f,i,fe,ie,k)
-                        mathematics.setmathcharacter(k,m,f,i,fe,ie)
+            elseif c then
+                local s = slots[k]
+                if s then
+                    local f, i, fe, ie = s[1], s[2], s[3], s[4]
+                    if mathematics.trace then
+                        trace(k,c,f,i,fe,ie)
                     end
-                elseif c then
-                    local s = slots[k]
+                    mathematics.setmathsymbol(c,m,f,i,fe,ie,k)
+                    mathematics.setmathcharacter(k,m,f,i,fe,ie)
+                end
+            elseif v.contextname then
+                local s = slots[k]
+                local c = v.contextname
+                if s then
+                    local f, i, fe, ie = s[1], s[2], s[3], s[4]
+                    if mathematics.trace then
+                        trace(k,c,f,i,fe,ie)
+                    end
+                    -- todo: mathortext
+                    -- mathematics.setmathsymbol(c,m,f,i,fe,ie,k)
+                    mathematics.setmathcharacter(k,m,f,i,fe,ie)
+                end
+            else
+                local a = v.adobename
+                if a and m then
+                    local s, f, i, fe, ie = slots[k], nil, nil, nil, nil
                     if s then
-                        local f, i, fe, ie = s[1], s[2], s[3], s[4]
-                        if mathematics.trace then
-                            trace(k,c,f,i,fe,ie)
-                        end
-                        mathematics.setmathsymbol(c,m,f,i,fe,ie,k)
-                        mathematics.setmathcharacter(k,m,f,i,fe,ie)
+                        f, i, fe, ie = s[1], s[2], s[3], s[4]
+                    elseif m == "variable" then
+                        f, i = mathematics.families.variables, k
+                    elseif m == "number" then
+                        f, i = mathematics.families.numbers, k
                     end
-                else
-                    local a = v.adobename
-                    if a and m then
-                        local s, f, i, fe, ie = slots[k], nil, nil, nil, nil
-                        if s then
-                            f, i, fe, ie = s[1], s[2], s[3], s[4]
-                        elseif m == "variable" then
-                            f, i = mathematics.families.variables, k
-                        elseif m == "number" then
-                            f, i = mathematics.families.numbers, k
-                        end
-                        if mathematics.trace then
-                            trace(k,a,f,i,fe,ie)
-                        end
-                        mathematics.setmathcharacter(k,m,f,i,fe,ie)
+                    if mathematics.trace then
+                        trace(k,a,f,i,fe,ie)
                     end
+                    mathematics.setmathcharacter(k,m,f,i,fe,ie)
                 end
             end
         end
@@ -296,9 +294,20 @@ mathematics.slots.traditional = {
     [0x00B1] = { "sy", 0x06 }, -- pm
     [0x00B7] = { "sy", 0x01 }, -- cdot
     [0x00D7] = { "sy", 0x02 }, -- times
+    [0x2022] = { "sy", 0x0F }, -- bullet
+
+    [0x2190] = { "sy", 0x20 }, -- leftarrow
+    [0x2192] = { "sy", 0x21 }, -- rightarrow
+    [0x2194] = { "sy", 0x24 }, -- leftrightarrow
+
+    [0x21D0] = { "sy", 0x28 }, -- Leftarrow
+    [0x21D2] = { "sy", 0x29 }, -- Rightarrow
+    [0x21D4] = { "sy", 0x2C }, -- Leftrightarrow
+
+    [0x2135] = { "sy", 0x40 }, -- aleph
+    [0x2113] = { "mi", 0x60 }, -- ell
 
-    [0x02113] = { "mi", 0x60 }, -- ell
-    [0x02135] = { "sy", 0x40 }, -- aleph
+    [0x25B3] = { "sy", 0x34 }, -- triangle up
 
     [0x1D6A4] = { "mi", 0x7B }, -- imath
     [0x1D6A5] = { "mi", 0x7C }, -- jmath
diff --git a/tex/context/base/meta-pdf.mkiv b/tex/context/base/meta-pdf.mkiv
index 90b7b5dd8..eded7d59d 100644
--- a/tex/context/base/meta-pdf.mkiv
+++ b/tex/context/base/meta-pdf.mkiv
@@ -78,7 +78,7 @@
 % will be done better
 
 \def\MPStextext#1#2#3#4#5% if we clean up this plugin model, we can
-  {\def\MPtextdata{#3}%  % delegate the splitter to lua
+  {\def\MPtextdata{#3}%  % delegate the splitter to lua + redesign
    \def\MPtextsize{#2}%
    \def\lastMPmoveX{#4}%
    \def\lastMPmoveY{#5}%
diff --git a/tex/context/base/s-sys-01.tex b/tex/context/base/s-sys-01.tex
new file mode 100644
index 000000000..f695bd618
--- /dev/null
+++ b/tex/context/base/s-sys-01.tex
@@ -0,0 +1,42 @@
+% engine=luatex
+
+\startluacode
+function showmath()
+    local slots  = mathematics.slots.traditional
+    local escape = characters.filters.utf.private.escape
+    local data   = characters.data
+    tex.sprint(tex.ctxcatcodes,"\\starttabulate[|T|c|T|T|T|T||||]")
+    for _, v in ipairs(table.sortedkeys(slots)) do
+        local t, d, u = slots[v], data[v], escape(utf.char(v))
+        if t[3] and t[4] then
+            tex.sprint(tex.ctxcatcodes,string.format(
+                "\\NC 0x%04X\\NC $%s$\\NC %s\\NC 0x%04X\\NC %s\\NC 0x%04X\\NC %s\\NC %s\\NC %s\\NC\\NR",
+                v,u,t[1],t[2],t[3],t[4],d.mathclass or "",d.mathname or "",d.description))
+        else
+            tex.sprint(tex.ctxcatcodes,string.format(
+                "\\NC 0x%04X\\NC $%s$\\NC %s\\NC 0x%04X\\NC\\NC\\NC %s\\NC %s\\NC %s\\NC\\NR",
+                v,u,t[1],t[2],d.mathclass or "",d.mathname or "",d.description))
+        end
+    end
+    tex.sprint(tex.ctxcatcodes,"\\stoptabulate")
+end
+\stopluacode
+
+\setuplayout
+  [backspace=1cm,
+   topspace=1cm,
+   footer=1cm,
+   header=0cm,
+   height=middle,
+   width=middle]
+
+\setupbodyfont
+  [9pt]
+
+\setupfootertexts
+  []
+  [math characters -- \pagenumber]
+
+\starttext
+    \ctxlua{showmath()}
+\stoptext
diff --git a/tex/context/base/x-mmp.mkiv b/tex/context/base/x-mmp.mkiv
index a0fed4bd6..7b96b0d30 100644
--- a/tex/context/base/x-mmp.mkiv
+++ b/tex/context/base/x-mmp.mkiv
@@ -168,8 +168,7 @@
          {\gdef\dodoMMLfiller{\enablefiller#1}%
           \let\normalorfiller\gobbletwoarguments}%
        $#1$}%
-    \popmacro\doMMLfiller
-    \dodoMMLfiller}
+    \popmacro\doMMLfiller}
 
 % setups
 
@@ -319,16 +318,18 @@
     \hskip.5em\relax
 \stopsetups
 
+% {} around first component is essential!
+
 \startsetups mml:msub
-    \xmlindex{#1}{/*}{1}_{\xmlindex{#1}{/*}{2}}
+    {\xmlindex{#1}{/*}{1}}_{\xmlindex{#1}{/*}{2}}
 \stopsetups
 
 \startsetups mml:msup
-    \xmlindex{#1}{/*}{1}^{\xmlindex{#1}{/*}{2}}
+    {\xmlindex{#1}{/*}{1}}^{\xmlindex{#1}{/*}{2}}
 \stopsetups
 
 \startsetups mml:msubsup
-    \xmlindex{#1}{/*}{1}_{\xmlindex{#1}{/*}{2}}^{\xmlindex{#1}{/*}{3}}
+    {\xmlindex{#1}{/*}{1}}_{\xmlindex{#1}{/*}{2}}^{\xmlindex{#1}{/*}{3}}
 \stopsetups
 
 \startsetups mml:mover
diff --git a/tex/context/interface/keys-ro.xml b/tex/context/interface/keys-ro.xml
index e39c4f5cc..9eeb40772 100644
--- a/tex/context/interface/keys-ro.xml
+++ b/tex/context/interface/keys-ro.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
-- 
cgit v1.2.3