From 86301645de1fc594ca94a2a722fce813c16966b1 Mon Sep 17 00:00:00 2001
From: Marius <mariausol@gmail.com>
Date: Mon, 14 May 2012 10:40:15 +0300
Subject: beta 2012.05.14 09:19

---
 scripts/context/lua/mtx-server-ctx-help.lua        |   1 +
 scripts/context/lua/mtx-server.lua                 |  60 ++---
 scripts/context/lua/mtxrun.lua                     |  24 +-
 scripts/context/stubs/mswin/mtxrun.lua             |  24 +-
 scripts/context/stubs/unix/mtxrun                  |  24 +-
 tex/context/base/cont-new.mkii                     |   2 +-
 tex/context/base/cont-new.mkiv                     |   2 +-
 tex/context/base/context-version.pdf               | Bin 4077 -> 4073 bytes
 tex/context/base/context-version.png               | Bin 105837 -> 105608 bytes
 tex/context/base/context.mkii                      |   2 +-
 tex/context/base/context.mkiv                      |   2 +-
 tex/context/base/data-bin.lua                      |   4 +-
 tex/context/base/data-zip.lua                      |  12 +
 tex/context/base/font-ctx.lua                      |  13 ++
 tex/context/base/font-sty.mkvi                     |  10 +-
 tex/context/base/l-dir.lua                         |  12 +-
 tex/context/base/lpdf-epd.lua                      |  16 ++
 tex/context/base/luat-exe.lua                      |  64 ++++--
 tex/context/base/luat-iop.lua                      | 243 ++++++++++++---------
 tex/context/base/mlib-run.lua                      |  65 ++++--
 tex/context/base/status-files.pdf                  | Bin 24332 -> 24330 bytes
 tex/context/base/status-lua.pdf                    | Bin 178832 -> 178784 bytes
 tex/context/base/strc-con.mkvi                     |   3 +-
 tex/generic/context/luatex/luatex-fonts-merged.lua |   2 +-
 web2c/contextcnf.lua                               |  37 +++-
 25 files changed, 424 insertions(+), 198 deletions(-)

diff --git a/scripts/context/lua/mtx-server-ctx-help.lua b/scripts/context/lua/mtx-server-ctx-help.lua
index 15f393853..a212e1369 100644
--- a/scripts/context/lua/mtx-server-ctx-help.lua
+++ b/scripts/context/lua/mtx-server-ctx-help.lua
@@ -7,6 +7,7 @@ if not modules then modules = { } end modules ['mtx-server-ctx-help'] = {
 }
 
 -- todo in lua interface: noargument, oneargument, twoarguments, threearguments
+-- todo: pickup translations from mult file
 
 --~ dofile(resolvers.findfile("l-aux.lua","tex"))
 --~ dofile(resolvers.findfile("l-url.lua","tex"))
diff --git a/scripts/context/lua/mtx-server.lua b/scripts/context/lua/mtx-server.lua
index 4547877b5..068d51111 100644
--- a/scripts/context/lua/mtx-server.lua
+++ b/scripts/context/lua/mtx-server.lua
@@ -29,7 +29,8 @@ scripts.webserver = scripts.webserver or { }
 dofile(resolvers.findfile("l-url.lua","tex"))
 dofile(resolvers.findfile("luat-soc.lua","tex"))
 
-local socket = socket or require("socket") -- redundant in future version
+local socket = socket or require("socket")
+local http   = socket or require("socket.http")
 local format = string.format
 
 -- The following two lists are taken from webrick (ruby) and
@@ -300,45 +301,50 @@ function scripts.webserver.run(configuration)
     report("scripts subpath: %s",configuration.scripts)
     report("context services: http://localhost:%s/mtx-server-ctx-startup.lua",configuration.port)
     local server = assert(socket.bind("*", configuration.port))
---~ local reading = { server }
+-- local reading = { server }
     while true do -- no multiple clients
         local start = os.clock()
---~ local input = socket.select(reading)
---~ local client = input:accept()
+-- local input = socket.select(reading)
+-- local client = input:accept()
         local client = server:accept()
         client:settimeout(configuration.timeout or 60)
         local request, e = client:receive()
+--         local request, e = client:receive("*a") -- doesn't work well (so no post)
         if e then
             errormessage(client,configuration,404)
         else
             local from = client:getpeername()
             report("request from: %s",tostring(from))
-            local fullurl = request:match("GET (.+) HTTP/.*$") -- todo: more clever
-            fullurl = socket.url.unescape(fullurl)
-            local hashed = url.hashed(fullurl)
-            local query = url.query(hashed.query)
-            local filename = hashed.path
---~ table.print(hashed)
-            if filename then
-                filename = socket.url.unescape(filename)
-                report("requested action: %s",filename)
-                if filename:find("%.%.") then
-                    filename = nil -- invalid path
-                end
-                if filename == nil or filename == "" or filename == "/" then
-                    filename = configuration.index
-                    report("invalid filename, forcing: %s",filename)
-                end
-                local suffix = file.extname(filename)
-                local action = handlers[suffix] or handlers.generic
-                if action then
-                    report("performing action: %s",filename)
-                    action(client,configuration,filename,suffix,false,hashed) -- filename and no content
+            local fullurl = request:match("GET (.+) HTTP/.*$") or "" -- todo: more clever / post
+            if fullurl == "" then
+                errormessage(client,configuration,404)
+            else
+                fullurl = socket.url.unescape(fullurl)
+                local hashed = url.hashed(fullurl)
+                local query = url.query(hashed.query)
+                local filename = hashed.path
+-- table.print(hashed)
+                if filename then
+                    filename = socket.url.unescape(filename)
+                    report("requested action: %s",filename)
+                    if filename:find("%.%.") then
+                        filename = nil -- invalid path
+                    end
+                    if filename == nil or filename == "" or filename == "/" then
+                        filename = configuration.index
+                        report("invalid filename, forcing: %s",filename)
+                    end
+                    local suffix = file.extname(filename)
+                    local action = handlers[suffix] or handlers.generic
+                    if action then
+                        report("performing action: %s",filename)
+                        action(client,configuration,filename,suffix,false,hashed) -- filename and no content
+                    else
+                        errormessage(client,configuration,404)
+                    end
                 else
                     errormessage(client,configuration,404)
                 end
-            else
-                errormessage(client,configuration,404)
             end
         end
         client:close()
diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua
index 69722cc55..716615039 100644
--- a/scripts/context/lua/mtxrun.lua
+++ b/scripts/context/lua/mtxrun.lua
@@ -3321,8 +3321,6 @@ local attributes = lfs.attributes
 local walkdir    = lfs.dir
 local isdir      = lfs.isdir
 local isfile     = lfs.isfile
-local mkdir      = lfs.mkdir
-local chdir      = lfs.chdir
 local currentdir = lfs.currentdir
 
 -- handy
@@ -3559,7 +3557,7 @@ if onwindows then
                 pth = pth .. "/" .. s
             end
             if make_indeed and not isdir(pth) then
-                mkdir(pth)
+                lfs.mkdir(pth)
             end
         end
         return pth, (isdir(pth) == true)
@@ -3591,7 +3589,7 @@ else
                     pth = pth .. "/" .. s
                 end
                 if make_indeed and not first and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         else
@@ -3599,7 +3597,7 @@ else
             for s in gmatch(str,"[^/]+") do
                 pth = pth .. "/" .. s
                 if make_indeed and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         end
@@ -3627,10 +3625,10 @@ if onwindows then
             first, last = match(str,"^([a-zA-Z]:)(.*)$")
             if first and not find(last,"^/") then
                 local d = currentdir()
-                if chdir(first) then
+                if lfs.chdir(first) then
                     first = dir.current()
                 end
-                chdir(d)
+                lfs.chdir(d)
             end
         end
         if not first then
@@ -14435,6 +14433,18 @@ local archives        = zip.archives
 zip.registeredfiles   = zip.registeredfiles or { }
 local registeredfiles = zip.registeredfiles
 
+local limited = false
+
+directives.register("system.inputmode", function(v)
+    if not limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            zip.open = i_limiter.protect(zip.open)
+            limited = true
+        end
+    end
+end)
+
 local function validzip(str) -- todo: use url splitter
     if not find(str,"^zip://") then
         return "zip:///" .. str
diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua
index 69722cc55..716615039 100644
--- a/scripts/context/stubs/mswin/mtxrun.lua
+++ b/scripts/context/stubs/mswin/mtxrun.lua
@@ -3321,8 +3321,6 @@ local attributes = lfs.attributes
 local walkdir    = lfs.dir
 local isdir      = lfs.isdir
 local isfile     = lfs.isfile
-local mkdir      = lfs.mkdir
-local chdir      = lfs.chdir
 local currentdir = lfs.currentdir
 
 -- handy
@@ -3559,7 +3557,7 @@ if onwindows then
                 pth = pth .. "/" .. s
             end
             if make_indeed and not isdir(pth) then
-                mkdir(pth)
+                lfs.mkdir(pth)
             end
         end
         return pth, (isdir(pth) == true)
@@ -3591,7 +3589,7 @@ else
                     pth = pth .. "/" .. s
                 end
                 if make_indeed and not first and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         else
@@ -3599,7 +3597,7 @@ else
             for s in gmatch(str,"[^/]+") do
                 pth = pth .. "/" .. s
                 if make_indeed and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         end
@@ -3627,10 +3625,10 @@ if onwindows then
             first, last = match(str,"^([a-zA-Z]:)(.*)$")
             if first and not find(last,"^/") then
                 local d = currentdir()
-                if chdir(first) then
+                if lfs.chdir(first) then
                     first = dir.current()
                 end
-                chdir(d)
+                lfs.chdir(d)
             end
         end
         if not first then
@@ -14435,6 +14433,18 @@ local archives        = zip.archives
 zip.registeredfiles   = zip.registeredfiles or { }
 local registeredfiles = zip.registeredfiles
 
+local limited = false
+
+directives.register("system.inputmode", function(v)
+    if not limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            zip.open = i_limiter.protect(zip.open)
+            limited = true
+        end
+    end
+end)
+
 local function validzip(str) -- todo: use url splitter
     if not find(str,"^zip://") then
         return "zip:///" .. str
diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun
index 69722cc55..716615039 100644
--- a/scripts/context/stubs/unix/mtxrun
+++ b/scripts/context/stubs/unix/mtxrun
@@ -3321,8 +3321,6 @@ local attributes = lfs.attributes
 local walkdir    = lfs.dir
 local isdir      = lfs.isdir
 local isfile     = lfs.isfile
-local mkdir      = lfs.mkdir
-local chdir      = lfs.chdir
 local currentdir = lfs.currentdir
 
 -- handy
@@ -3559,7 +3557,7 @@ if onwindows then
                 pth = pth .. "/" .. s
             end
             if make_indeed and not isdir(pth) then
-                mkdir(pth)
+                lfs.mkdir(pth)
             end
         end
         return pth, (isdir(pth) == true)
@@ -3591,7 +3589,7 @@ else
                     pth = pth .. "/" .. s
                 end
                 if make_indeed and not first and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         else
@@ -3599,7 +3597,7 @@ else
             for s in gmatch(str,"[^/]+") do
                 pth = pth .. "/" .. s
                 if make_indeed and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         end
@@ -3627,10 +3625,10 @@ if onwindows then
             first, last = match(str,"^([a-zA-Z]:)(.*)$")
             if first and not find(last,"^/") then
                 local d = currentdir()
-                if chdir(first) then
+                if lfs.chdir(first) then
                     first = dir.current()
                 end
-                chdir(d)
+                lfs.chdir(d)
             end
         end
         if not first then
@@ -14435,6 +14433,18 @@ local archives        = zip.archives
 zip.registeredfiles   = zip.registeredfiles or { }
 local registeredfiles = zip.registeredfiles
 
+local limited = false
+
+directives.register("system.inputmode", function(v)
+    if not limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            zip.open = i_limiter.protect(zip.open)
+            limited = true
+        end
+    end
+end)
+
 local function validzip(str) -- todo: use url splitter
     if not find(str,"^zip://") then
         return "zip:///" .. str
diff --git a/tex/context/base/cont-new.mkii b/tex/context/base/cont-new.mkii
index 8797cb81e..59c540b93 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.05.11 13:19}
+\newcontextversion{2012.05.14 09:19}
 
 %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 3296f06da..d04de763b 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.05.11 13:19}
+\newcontextversion{2012.05.14 09:19}
 
 %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/context-version.pdf b/tex/context/base/context-version.pdf
index 73ddb663a..4fe170955 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 76b308b51..be09ece3b 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 a327be3e5..e0433384f 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.05.11 13:19}
+\edef\contextversion{2012.05.14 09:19}
 
 %D For those who want to use this:
 
diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv
index 9c1146866..bebc6dee7 100644
--- a/tex/context/base/context.mkiv
+++ b/tex/context/base/context.mkiv
@@ -23,7 +23,7 @@
 %D up and the dependencies are more consistent.
 
 \edef\contextformat {\jobname}
-\edef\contextversion{2012.05.11 13:19}
+\edef\contextversion{2012.05.14 09:19}
 
 %D For those who want to use this:
 
diff --git a/tex/context/base/data-bin.lua b/tex/context/base/data-bin.lua
index b18526c77..1d1e8b749 100644
--- a/tex/context/base/data-bin.lua
+++ b/tex/context/base/data-bin.lua
@@ -14,13 +14,13 @@ function resolvers.findbinfile(filename,filetype)
 end
 
 function resolvers.openbinfile(filename)
-    return methodhandler('loaders',filename)
+    return methodhandler('loaders',filename) -- a bit weird: load
 end
 
 function resolvers.loadbinfile(filename,filetype)
     local fname = methodhandler('finders',filename,filetype)
     if fname and fname ~= "" then
-        return resolvers.openbinfile(fname)
+        return resolvers.openbinfile(fname) -- a bit weird: open
     else
         return resolvers.loaders.notfound()
     end
diff --git a/tex/context/base/data-zip.lua b/tex/context/base/data-zip.lua
index 6a50d2aa9..1d2f2d4f6 100644
--- a/tex/context/base/data-zip.lua
+++ b/tex/context/base/data-zip.lua
@@ -31,6 +31,18 @@ local archives        = zip.archives
 zip.registeredfiles   = zip.registeredfiles or { }
 local registeredfiles = zip.registeredfiles
 
+local limited = false
+
+directives.register("system.inputmode", function(v)
+    if not limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            zip.open = i_limiter.protect(zip.open)
+            limited = true
+        end
+    end
+end)
+
 local function validzip(str) -- todo: use url splitter
     if not find(str,"^zip://") then
         return "zip:///" .. str
diff --git a/tex/context/base/font-ctx.lua b/tex/context/base/font-ctx.lua
index 5680ee69e..4b324a6db 100644
--- a/tex/context/base/font-ctx.lua
+++ b/tex/context/base/font-ctx.lua
@@ -99,6 +99,19 @@ local nulldata = {
 
 constructors.enhanceparameters(nulldata.parameters) -- official copies for us
 
+local limited = false
+
+directives.register("system.inputmode", function(v)
+    if not limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            fontloader.open = i_limiter.protect(fontloader.open)
+            fontloader.info = i_limiter.protect(fontloader.info)
+            limited = true
+        end
+    end
+end)
+
 function definers.resetnullfont()
     -- resetting is needed because tikz misuses nullfont
     local parameters = nulldata.parameters
diff --git a/tex/context/base/font-sty.mkvi b/tex/context/base/font-sty.mkvi
index 9291f3b0a..d8f01afa7 100644
--- a/tex/context/base/font-sty.mkvi
+++ b/tex/context/base/font-sty.mkvi
@@ -157,7 +157,15 @@
      \doubleexpandafter\m_current_convert_font
    \fi\fi}
 
-%D Low level switches (downward compatible):
+%D Low level switches (downward compatible, but we keep them as one can use
+%D them in styles):
+%D
+%D \starttyping
+%D \usemodule[abr-02]
+%D \setuphead[chapter][style=\bfb]
+%D \setupfooter[style=\dontconvertfont\bf]
+%D \chapter{This is \TEX}
+%D \stoptyping
 
 \unexpanded\def\dontconvertfont{\c_font_current_alternative_style_index\plustwo} % needs checking in usage
 \unexpanded\def\redoconvertfont{\c_font_current_alternative_style_index\plusone} % needs checking in usage
diff --git a/tex/context/base/l-dir.lua b/tex/context/base/l-dir.lua
index ee831f98a..71de3114e 100644
--- a/tex/context/base/l-dir.lua
+++ b/tex/context/base/l-dir.lua
@@ -23,8 +23,6 @@ local attributes = lfs.attributes
 local walkdir    = lfs.dir
 local isdir      = lfs.isdir
 local isfile     = lfs.isfile
-local mkdir      = lfs.mkdir
-local chdir      = lfs.chdir
 local currentdir = lfs.currentdir
 
 -- handy
@@ -292,7 +290,7 @@ if onwindows then
                 pth = pth .. "/" .. s
             end
             if make_indeed and not isdir(pth) then
-                mkdir(pth)
+                lfs.mkdir(pth)
             end
         end
         return pth, (isdir(pth) == true)
@@ -335,7 +333,7 @@ else
                     pth = pth .. "/" .. s
                 end
                 if make_indeed and not first and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         else
@@ -343,7 +341,7 @@ else
             for s in gmatch(str,"[^/]+") do
                 pth = pth .. "/" .. s
                 if make_indeed and not isdir(pth) then
-                    mkdir(pth)
+                    lfs.mkdir(pth)
                 end
             end
         end
@@ -378,10 +376,10 @@ if onwindows then
             first, last = match(str,"^([a-zA-Z]:)(.*)$")
             if first and not find(last,"^/") then
                 local d = currentdir()
-                if chdir(first) then
+                if lfs.chdir(first) then
                     first = dir.current()
                 end
-                chdir(d)
+                lfs.chdir(d)
             end
         end
         if not first then
diff --git a/tex/context/base/lpdf-epd.lua b/tex/context/base/lpdf-epd.lua
index 3e917dba2..76d258cef 100644
--- a/tex/context/base/lpdf-epd.lua
+++ b/tex/context/base/lpdf-epd.lua
@@ -27,6 +27,22 @@ local lower, match, char, find, sub = string.lower, string.match, string.char, s
 local concat = table.concat
 local toutf = string.toutf
 
+-- a bit of protection
+
+local limited = false
+
+directives.register("system.inputmode", function(v)
+    if not limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            epdf.open = i_limiter.protect(epdf.open)
+            limited = true
+        end
+    end
+end)
+
+--
+
 function epdf.type(o)
     local t = lower(match(tostring(o),"[^ :]+"))
     return t or "?"
diff --git a/tex/context/base/luat-exe.lua b/tex/context/base/luat-exe.lua
index 42c17ded5..0e9a94313 100644
--- a/tex/context/base/luat-exe.lua
+++ b/tex/context/base/luat-exe.lua
@@ -17,8 +17,16 @@ resolvers.executers = resolvers.executers or { }
 local executers     = resolvers.executers
 
 local permitted     = { }
+
 local osexecute     = os.execute
+local osexec        = os.exec
+local osspawn       = os.spawn
+local iopopen       = io.popen
+
 local execute       = osexecute
+local exec          = osexec
+local spawn         = osspawn
+local popen         = iopopen
 
 local function register(...)
     local t = { ... }
@@ -28,33 +36,47 @@ local function register(...)
     end
 end
 
-local function finalize() -- todo: os.exec, todo: report ipv print
-    execute = function(...)
-        -- todo: make more clever first split
-        local t, name, arguments = { ... }, "", ""
-        local one = t[1]
-        if #t == 1 then
-            if type(one) == 'table' then
-                name, arguments = one, concat(t," ",2,#t)
+local function prepare(...)
+    -- todo: make more clever first split
+    local t = { ... }
+    local one = t[1]
+    if #t == 1 then
+        if type(one) == 'table' then
+            return one, concat(t," ",2,#t)
+        else
+            local name, arguments = match(one,"^(.-)%s+(.+)$")
+            if name and arguments then
+                return name, arguments
             else
-                name, arguments = match(one,"^(.-)%s+(.+)$")
-                if not (name and arguments) then
-                    name, arguments = one, ""
-                end
+                return one, ""
             end
-        else
-            name, arguments = one, concat(t," ",2,#t)
         end
+    else
+        return one, concat(t," ",2,#t)
+    end
+end
+
+local function executer(action)
+    return function(...)
+        local name, arguments = prepare(...)
         for k=1,#permitted do
             local v = permitted[k]
             if find(name,v) then
-                osexecute(name .. " " .. arguments)
+                return action(name .. " " .. arguments)
             --  print("executed: " .. name .. " " .. arguments)
             else
                 report_executers("not permitted: %s %s",name,arguments)
             end
         end
+        return action("")
     end
+end
+
+local function finalize() -- todo: os.exec, todo: report ipv print
+    execute = executer(osexecute)
+    exec    = executer(osexec)
+    spawn   = executer(osspawn)
+    popen   = executer(iopopen)
     finalize = function()
         report_executers("already finalized")
     end
@@ -62,11 +84,17 @@ local function finalize() -- todo: os.exec, todo: report ipv print
         report_executers("already finalized, no registration permitted")
     end
     os.execute = execute
+    os.exec    = exec
+    os.spawn   = spawn
+    io.popen   = popen
 end
 
-executers.finalize = function(...) finalize(...) end
-executers.register = function(...) register(...) end
-executers.execute  = function(...) execute (...) end
+executers.finalize = function(...) return finalize(...) end
+executers.register = function(...) return register(...) end
+executers.execute  = function(...) return execute (...) end
+executers.exec     = function(...) return exec    (...) end
+executers.spawn    = function(...) return spawn   (...) end
+executers.popen    = function(...) return popen   (...) end
 
 local execution_mode  directives.register("system.executionmode", function(v) execution_mode = v end)
 local execution_list  directives.register("system.executionlist", function(v) execution_list = v end)
diff --git a/tex/context/base/luat-iop.lua b/tex/context/base/luat-iop.lua
index 091639de2..5512b258e 100644
--- a/tex/context/base/luat-iop.lua
+++ b/tex/context/base/luat-iop.lua
@@ -11,138 +11,185 @@ if not modules then modules = { } end modules ['luat-iop'] = {
 -- we can feed back specific patterns and paths into the next
 -- mechanism
 
-local lower, find, sub = string.lower, string.find, string.sub
+-- os.execute os.exec os.spawn io.fopen
+-- os.remove lfs.chdir lfs.mkdir
+-- io.open zip.open epdf.open mlib.new
 
-local allocate = utilities.storage.allocate
+-- cache
 
-local ioinp = io.inp if not ioinp then ioinp = { } io.inp = ioinp end
-local ioout = io.out if not ioout then ioout = { } io.out = ioout end
+local topattern, find = string.topattern, string.find
 
-ioinp.modes, ioout.modes = allocate(), allocate()
+local report_limiter = logs.reporter("system","limiter")
 
-local inp_blocked, inp_permitted = { }, { }
-local out_blocked, out_permitted = { }, { }
+-- the basic methods
 
-local function i_inhibit(name) inp_blocked  [#inp_blocked  +1] = name end
-local function o_inhibit(name) out_blocked  [#out_blocked  +1] = name end
-local function i_permit (name) inp_permitted[#inp_permitted+1] = name end
-local function o_permit (name) out_permitted[#out_permitted+1] = name end
-
-ioinp.inhibit, ioinp.permit = i_inhibit, o_permit
-ioout.inhibit, ioout.permit = o_inhibit, o_permit
-
-local blockedopeners = { } -- *.open(name,method)
-
-function io.registeropener(func)
-    blockedopeners[#blockedopeners+1] = func
+local function match(ruleset,name)
+    local n = #ruleset
+    if n > 0 then
+        for i=1,n do
+            local r = ruleset[i]
+            if find(name,r[1]) then
+                return r[2]
+            end
+        end
+        return false
+    else
+        -- nothing defined (or any)
+        return true
+    end
 end
 
-local function checked(name,blocked,permitted)
-    local n = lower(name)
-    for _,b in next, blocked do
-        if find(n,b) then
-            for _,p in next, permitted do
-                if find(n,p) then
-                    return true
-                end
-            end
-            return false
+local function protect(ruleset,proc)
+    return function(name,...)
+        if name == "" then
+         -- report_limiter("no access permitted: <no name>") -- can happen in mplib code
+            return nil, "no name given"
+        elseif match(ruleset,name) then
+            return proc(name,...)
+        else
+            report_limiter("no access permitted: %s",name)
+            return nil, name .. ": no access permitted"
         end
     end
-    return true
 end
 
-function io.finalizeopeners(func)
-    if #out_blocked > 0 or #inp_blocked > 0 then
-        local open = func -- why not directly?
-        return function(name,method)
-            if method and find(method,'[wa]') then
-                if #out_blocked > 0 and not checked(name,out_blocked,out_permitted) then
-                    -- print("writing to " .. name .. " is not permitted")
-                    return nil
+function io.limiter(preset)
+    preset = preset or { }
+    local ruleset = { }
+    for i=1,#preset do
+        local p = preset[i]
+        local what, spec = p[1] or "", p[2] or ""
+        if spec == "" then
+            -- skip 'm
+        elseif what == "tree" then
+            resolvers.dowithpath(spec, function(r)
+                local spec = resolvers.resolve(r) or ""
+                if spec ~= "" then
+                    ruleset[#ruleset+1] = { topattern(spec,true), true }
                 end
-            else
-                if #inp_blocked > 0 and not checked(name,inp_blocked,inp_permitted) then
-                    -- print("reading from " .. name .. " is not permitted")
-                    return nil
-                end
-            end
-            return open(name,method)
+            end)
+        elseif what == "permit" then
+            ruleset[#ruleset+1] = { topattern(spec,true), true }
+        elseif what == "forbid" then
+            ruleset[#ruleset+1] = { topattern(spec,true), false }
         end
+    end
+    if #ruleset > 0 then
+        return {
+            match   = function(name) return match  (ruleset,name) end,
+            protect = function(proc) return protect(ruleset,proc) end,
+        }
     else
-        return func
+        return {
+            match   = function(name) return true end,
+            protect = proc,
+        }
     end
 end
 
---~ io.inp.inhibit('^%.')
---~ io.inp.inhibit('^/etc')
---~ io.inp.inhibit('/windows/')
---~ io.inp.inhibit('/winnt/')
---~ io.inp.permit('c:/windows/wmsetup.log')
-
---~ io.open = io.finalizeopeners(io.open)
+-- a few handlers
 
---~ f = io.open('.tex')                   print(f)
---~ f = io.open('tufte.tex')              print(f)
---~ f = io.open('t:/sources/tufte.tex')   print(f)
---~ f = io.open('/etc/passwd')            print(f)
---~ f = io.open('c:/windows/crap.log')    print(f)
---~ f = io.open('c:/windows/wmsetup.log') print(f)
+io.i_limiters = { }
+io.o_limiters = { }
 
--- restricted
-
-function ioinp.modes.restricted()
-    i_inhibit('^%.[%a]')
+function io.i_limiter(v)
+    local i = io.i_limiters[v]
+    if i then
+        local i_limiter = io.limiter(i)
+        function io.i_limiter()
+            return i_limiter
+        end
+        return i_limiter
+    end
 end
 
-function ioout.modes.restricted()
-    o_inhibit('^%.[%a]')
+function io.o_limiter(v)
+    local o = io.o_limiters[v]
+    if o then
+        local o_limiter = io.limiter(o)
+        function io.o_limiter()
+            return o_limiter
+        end
+        return o_limiter
+    end
 end
 
--- paranoid
+-- the real thing (somewhat fuzzy as we need to know what gets done)
 
-function ioinp.modes.paranoid()
-    i_inhibit('.*')
-    i_inhibit('%.%.')
-    i_permit('^%./')
-    i_permit('[^/]')
-    resolvers.dowithpath('TEXMF',i_permit)
-end
+local i_opener, i_limited = io.open, false
+local o_opener, o_limited = io.open, false
 
-function ioout.modes.paranoid()
-    o_inhibit('.*')
-    resolvers.dowithpath('TEXMFOUTPUT',o_permit)
+local function i_register(v)
+    if not i_limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            local protect = i_limiter.protect
+            i_opener = protect(i_opener)
+            i_limited = true
+            report_limiter("input mode: %s",v)
+        end
+    end
 end
 
--- handy
+local function o_register(v)
+    if not o_limited then
+        local o_limiter = io.o_limiter(v)
+        if o_limiter then
+            local protect = o_limiter.protect
+            o_opener = protect(o_opener)
+            o_limited = true
+            report_limiter("output mode: %s",v)
+        end
+    end
+end
 
-function ioinp.modes.handy()
-    i_inhibit('%.%.')
-    if os.type == 'windows' then
-        i_inhibit('/windows/')
-        i_inhibit('/winnt/')
+function io.open(name,method)
+    if method and find(method,"[wa]") then
+        return o_opener(name,method)
     else
-        i_inhibit('^/etc')
+        return i_opener(name,method)
     end
 end
 
-function ioout.modes.handy()
-    o_inhibit('.*')
-    o_permit('%./')
-    o_permit('^%./')
-    o_permit('[^/]')
+directives.register("system.inputmode",  i_register)
+directives.register("system.outputmode", o_register)
+
+local i_limited = false
+local o_limited = false
+
+local function i_register(v)
+    if not i_limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            local protect = i_limiter.protect
+            lfs.chdir = protect(lfs.chdir) -- needs checking
+            i_limited = true
+        end
+    end
+end
+
+local function o_register(v)
+    if not o_limited then
+        local o_limiter = io.o_limiter(v)
+        if o_limiter then
+            local protect = o_limiter.protect
+            os.remove = protect(os.remove) -- rather okay
+            lfs.chdir = protect(lfs.chdir) -- needs checking
+            lfs.mkdir = protect(lfs.mkdir) -- needs checking
+            o_limited = true
+        end
+    end
 end
 
-local input_mode   directives.register("system.inputmode",  function(v) input_mode  = v end)
-local output_mode  directives.register("system.outputmode", function(v) output_mode = v end)
+directives.register("system.inputmode",  i_register)
+directives.register("system.outputmode", o_register)
+
+-- the definitions
+
+local limiters = resolvers.variable("limiters")
 
-function io.checkopeners()
-    local inp = input_mode  or resolvers.variable("input_mode")  -- or ... will become obsolete
-    local out = output_mode or resolvers.variable("output_mode") -- or ... will become obsolete
-    inp = inp and ioinp.modes[inp]
-    out = out and ioinp.modes[out]
-    if inp then inp() end
-    if out then out() end
+if limiters then
+    io.i_limiters = limiters.input  or { }
+    io.o_limiters = limiters.output or { }
 end
 
---~ io.checkopeners()
diff --git a/tex/context/base/mlib-run.lua b/tex/context/base/mlib-run.lua
index dd172f884..f42b53594 100644
--- a/tex/context/base/mlib-run.lua
+++ b/tex/context/base/mlib-run.lua
@@ -58,30 +58,63 @@ function metapost.resetlastlog()
     metapost.lastlog = ""
 end
 
+-- local function realfinder(name, mode, ftype)
+--     if mode == "w" then
+--         return name
+--     elseif file.is_qualified_path(name) then
+--         return name
+--     else
+--         return resolvers.findfile(name,ftype)
+--     end
+-- end
+
+local function i_finder(name, mode, ftype) -- fake message for mpost.map and metafun.mpvi
+    name = file.is_qualified_path(name) and name or resolvers.findfile(name,ftype)
+    if not (find(name,"/metapost/context/base/") or find(name,"/metapost/context/") or find(name,"/metapost/base/")) then
+        local data, found, forced = metapost.checktexts(io.loaddata(name) or "")
+        if found then
+            local temp = luatex.registertempfile(name,true)
+            io.savedata(temp,data)
+            name = temp
+        end
+    end
+    return name
+end
+
+local function o_finder(name, mode, ftype)
+    return name
+end
+
 local function finder(name, mode, ftype)
     if mode == "w" then
-        return name
-    elseif file.is_qualified_path(name) then
-        return name
+        return o_finder(name, mode, ftype)
     else
-        return resolvers.findfile(name,ftype)
+        return i_finder(name, mode, ftype)
     end
 end
 
-local function finder(name, mode, ftype)
-    if mode ~= "w" then
-        name = file.is_qualified_path(name) and name or resolvers.findfile(name,ftype)
-        if not (find(name,"/metapost/context/base/") or find(name,"/metapost/context/") or find(name,"/metapost/base/")) then
-            local data, found, forced = metapost.checktexts(io.loaddata(name) or "")
-            if found then
-                local temp = luatex.registertempfile(name,true)
-                io.savedata(temp,data)
-                name = temp
-            end
+local i_limited = false
+local o_limited = false
+
+directives.register("system.inputmode", function(v)
+    if not i_limited then
+        local i_limiter = io.i_limiter(v)
+        if i_limiter then
+            i_finder = i_limiter.protect(i_finder)
+            i_limited = true
         end
     end
-    return name
-end
+end)
+
+directives.register("system.outputmode", function(v)
+    if not o_limited then
+        local o_limiter = io.o_limiter(v)
+        if o_limiter then
+            o_finder = o_limiter.protect(o_finder)
+            o_limited = true
+        end
+    end
+end)
 
 -- -- --
 
diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf
index 4cb9e63b8..0ab6170e3 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 5c252820b..ec6d3a5cb 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-con.mkvi b/tex/context/base/strc-con.mkvi
index 72ca50189..3068fda0b 100644
--- a/tex/context/base/strc-con.mkvi
+++ b/tex/context/base/strc-con.mkvi
@@ -480,7 +480,8 @@
     \noindent
     \leftskip\leftconstructionskip
     \rightskip\dimexpr\rightconstructionskip+\constructionsheadwidth\relax
-    \strc_constructions_pure_hang_box\raggedleft
+   %\strc_constructions_pure_hang_box\raggedleft
+    \strc_constructions_set_pure_box\v!flushright
     \rlap {
         \hskip\dimexpr\hsize-\leftskip-\rightskip\relax
         \copy\constructionheadbox
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index b1d15c50d..a8de54f84 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  : 05/11/12 13:19:54
+-- merge date  : 05/14/12 09:19:00
 
 do -- begin closure to overcome local limits and interference
 
diff --git a/web2c/contextcnf.lua b/web2c/contextcnf.lua
index b89b7d34d..fe88bef1c 100644
--- a/web2c/contextcnf.lua
+++ b/web2c/contextcnf.lua
@@ -109,6 +109,39 @@ return {
             FONTCONFIG_FILE = "fonts.conf",
             FONTCONFIG_PATH = "$TEXMFSYSTEM/fonts/conf",
 
+            limiters = {
+                input = {
+                 -- any = {
+                 --     { "permit", "*"          },
+                 -- },
+                 -- restricted = {
+                 --     { "permit", "*"          },
+                 -- },
+                    paranoid = {
+                        { "permit", "^[^/]+$"    },
+                        { "permit", "^./"        },
+                        { "forbid", ".."         },
+                        { "tree"  , "TEXMF"      },
+                     -- { "tree"  , "MPINPUTS"   },
+                     -- { "tree"  , "TEXINPUTS"  },
+                        { "forbid", "^/.."       },
+                        { "forbid", "^[a-c]:/.." },
+                    },
+                },
+                output = {
+                 -- any = {
+                 --     { "permit", "*"          },
+                 -- },
+                 -- restricted = {
+                 --     { "permit", "*"          },
+                 -- },
+                    paranoid = {
+                        { "permit", "^[^/]+$"    },
+                        { "permit", "^./"        },
+                    },
+                }
+            },
+
         },
 
         -- We have a few reserved subtables. These control runtime behaviour. The
@@ -141,8 +174,8 @@ return {
             -- The io modes are similar to the traditional ones. Possible values
             -- are all, paranoid and restricted.
 
-            ["system.outputmode"]        = "restricted",
-            ["system.inputmode"]         = "any",
+         -- ["system.outputmode"]        = "restricted",
+         -- ["system.inputmode"]         = "any",
 
             -- The following variable is under consideration. We do have protection
             -- mechanims but it's not enabled by default.
-- 
cgit v1.2.3