texlive[64615] Master/texmf-dist: lualibs (4oct22)

commits+karl at tug.org commits+karl at tug.org
Tue Oct 4 21:55:24 CEST 2022


Revision: 64615
          http://tug.org/svn/texlive?view=revision&revision=64615
Author:   karl
Date:     2022-10-04 21:55:23 +0200 (Tue, 04 Oct 2022)
Log Message:
-----------
lualibs (4oct22)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/luatex/lualibs/NEWS
    trunk/Master/texmf-dist/doc/luatex/lualibs/README.md
    trunk/Master/texmf-dist/doc/luatex/lualibs/lualibs.pdf
    trunk/Master/texmf-dist/source/luatex/lualibs/lualibs.dtx
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended-merged.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua

Added Paths:
-----------
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sac.lua
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-zip.lua

Removed Paths:
-------------
    trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-gzip.lua

Modified: trunk/Master/texmf-dist/doc/luatex/lualibs/NEWS
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/lualibs/NEWS	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/doc/luatex/lualibs/NEWS	2022-10-04 19:55:23 UTC (rev 64615)
@@ -1,6 +1,12 @@
                         History of the lualibs package
+2022/10/04 v2.75/
+    * sync with Context current as of 2022/10/04.
+    * add util-sac
+    * Replace l-gzip with util-zip
+
 2021/05/20 v2.74/
     * sync with Context current as of 2021/05/20.                        
+
 2020/12/30 v2.73/
     * sync with Context current as of 2020/12/30.
                         

Modified: trunk/Master/texmf-dist/doc/luatex/lualibs/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/luatex/lualibs/README.md	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/doc/luatex/lualibs/README.md	2022-10-04 19:55:23 UTC (rev 64615)
@@ -1,10 +1,10 @@
 # The Lualibs Package
 
-VERSION: 2.74
+VERSION: 2.75
 
-DATE: 2021-05-20
+DATE: 2022-10-04
 
-FONTLOADERDATE: 2021-05-20
+FONTLOADERDATE: 2022-10-04
 
 Lualibs is a collection of Lua modules useful for general programming.
 
@@ -47,7 +47,6 @@
 |    lualibs-compat.lua              |tex/luatex/lualibs/lualibs-compat.lua
 |    lualibs-dir.lua                 |tex/luatex/lualibs/lualibs-dir.lua
 |    lualibs-file.lua                | tex/luatex/lualibs/lualibs-file.lua
-|    lualibs-gzip.lua                | tex/luatex/lualibs/lualibs-gzip.lua
 |    lualibs-function.lua            | tex/luatex/lualibs/lualibs-function.lua
 |    lualibs-io.lua                  | tex/luatex/lualibs/lualibs-io.lua
 |    lualibs-lpeg.lua                | tex/luatex/lualibs/lualibs-lpeg.lua
@@ -68,11 +67,13 @@
 |    lualibs-util-jsn.lua            | tex/luatex/lualibs/lualibs-util-jsn.lua
 |    lualibs-util-lua.lua            | tex/luatex/lualibs/lualibs-util-lua.lua
 |    lualibs-util-prs.lua            | tex/luatex/lualibs/lualibs-util-prs.lua
+|    lualibs-util-sac.lua            | tex/luatex/lualibs/lualibs-util-sac.lua
 |    lualibs-util-sta.lua            | tex/luatex/lualibs/lualibs-util-sta.lua
 |    lualibs-util-sto.lua            | tex/luatex/lualibs/lualibs-util-sto.lua
 |    lualibs-util-str.lua            | tex/luatex/lualibs/lualibs-util-str.lua
 |    lualibs-util-tab.lua            | tex/luatex/lualibs/lualibs-util-tab.lua
 |    lualibs-util-tpl.lua            | tex/luatex/lualibs/lualibs-util-tpl.lua
+|    lualibs-util-zip.lua            | tex/luatex/lualibs/lualibs-util-zip.lua
 |    LICENSE                         | doc/luatex/lualibs/LICENSE
 |    NEWS                            | doc/luatex/lualibs/NEWS
 |    README                          | doc/luatex/lualibs/README

Modified: trunk/Master/texmf-dist/doc/luatex/lualibs/lualibs.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/luatex/lualibs/lualibs.dtx
===================================================================
--- trunk/Master/texmf-dist/source/luatex/lualibs/lualibs.dtx	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/source/luatex/lualibs/lualibs.dtx	2022-10-04 19:55:23 UTC (rev 64615)
@@ -37,7 +37,7 @@
 \input docstrip.tex
 \Msg{************************************************************************}
 \Msg{* Installation}
-\Msg{* Package: lualibs 2021-05-20 v2.74 Lua additional functions.}
+\Msg{* Package: lualibs 2022-10-04 v2.75 Lua additional functions.}
 \Msg{************************************************************************}
 
 \keepsilent
@@ -107,7 +107,7 @@
 %<*driver>
 \NeedsTeXFormat{LaTeX2e}
 \ProvidesFile{lualibs.drv}
-  [2021/05/20 v2.74 Lua Libraries.]
+  [2022/10/04 v2.75 Lua Libraries.]
 \documentclass{ltxdoc}
 \usepackage{fancyvrb,xspace}
 \usepackage[x11names]{xcolor}
@@ -115,6 +115,7 @@
 \def\primarycolor{DodgerBlue4}  %%-> rgb  16  78 139 | #104e8b
 \def\secondarycolor{Goldenrod4} %%-> rgb 139 105 200 | #8b6914
 %
+\AddToHook{package/hyperref/after}{%
 \hypersetup{
    colorlinks=true,
     linkcolor=\primarycolor,
@@ -123,7 +124,7 @@
      pdftitle={The lualibs package},
    pdfsubject={Port of the ConTeXt Lua libraries},
     pdfauthor={Elie Roux & Philipp Gesang},
-  pdfkeywords={luatex, lualatex, unicode, opentype}}
+  pdfkeywords={luatex, lualatex, unicode, opentype}}}
 
 \newcommand*\email[1]{\href{mailto:#1}{#1}}
 \usepackage{fontspec}
@@ -207,7 +208,7 @@
 % \GetFileInfo{lualibs.drv}
 %
 % \title{The \identifier{lualibs} package}
-% \date{2021/05/20 v2.74}
+% \date{2022/10/04 v2.75}
 % \author{Élie Roux      · \email{elie.roux at telecom-bretagne.eu}\\
 %         Philipp Gesang · \email{phg at phi-gamma.net}\\
 %         The \LaTeX{} Project · \email{https://github.com/latex3/lualibs/}\\
@@ -322,12 +323,11 @@
 %   lualibs-io.lua            & l-io.lua       & reading and writing files      \\
 %   lualibs-os.lua            & l-os.lua       & platform specific code         \\
 %   lualibs-file.lua          & l-file.lua     & filesystem operations          \\
-%   lualibs-gzip.lua          & l-gzip.lua     & wrapper for \identifier{lgzip} \\
 %   lualibs-md5.lua           & l-md5.lua      & checksum functions             \\
 %   lualibs-dir.lua           & l-dir.lua      & directory handling             \\
 %   lualibs-unicode.lua       & l-unicode.lua  & utf and unicode                \\
 %   lualibs-url.lua           & l-url.lua      & url handling                   \\
-%   lualibs-set.lua           & l-set.lua      & sets                           \\[1ex]
+%   lualibs-set.lua           & l-set.lua      & sets                           \\
 %  \end{tabular}
 %  \label{tab:basic}
 %  \hrule
@@ -346,6 +346,7 @@
 %  \vskip1em
 %  \begin{tabular}{l l l}
 %   \identifier{lualibs} name & \CONTEXT name  & primary purpose                  \\[1ex]
+%   lualibs-util-sac.lua      & util-sac.lua   & string based file readers        \\
 %   lualibs-util-str.lua      & util-str.lua   & extra |string| functions         \\
 %   lualibs-util-fil.lua      & util-fil.lua   & extra |file| functions           \\
 %   lualibs-util-tab.lua      & util-tab.lua   & extra |table| functions          \\
@@ -357,7 +358,8 @@
 %   lualibs-util-deb.lua      & util-deb.lua   & extra |debug| functionality      \\
 %   lualibs-util-tpl.lua      & util-tpl.lua   & templating                       \\
 %   lualibs-util-sta.lua      & util-sta.lua   & stacker (e.~g. for \abbrev{pdf}) \\
-%   lualibs-util-jsn.lua      & util-jsn.lua   & conversion to and from json      \\[1ex]
+%   lualibs-util-jsn.lua      & util-jsn.lua   & conversion to and from json      \\
+%   lualibs-util-zip.lua      & util-zip.lua   & compression and zip files        \\[1ex]
 %  \end{tabular}
 %  \label{tab:extended}
 %  \hrule
@@ -428,8 +430,8 @@
 
 lualibs.module_info = {
   name          = "lualibs",
-  version       = "2.74",       --TAGVERSION
-    date        = "2021-05-20", --TAGDATE
+  version       = "2.75",       --TAGVERSION
+    date        = "2022-10-04", --TAGDATE
   description   = "ConTeXt Lua standard libraries.",
   author        = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang",
   copyright     = "PRAGMA ADE / ConTeXt Development Team",
@@ -582,8 +584,8 @@
 
 local lualibs_basic_module = {
   name          = "lualibs-basic",
-  version       = "2.74",       --TAGVERSION
-  date          = "2021-05-20", --TAGDATE
+  version       = "2.75",       --TAGVERSION
+  date          = "2022-10-04", --TAGDATE
   description   = "ConTeXt Lua libraries -- basic collection.",
   author        = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang",
   copyright     = "PRAGMA ADE / ConTeXt Development Team",
@@ -621,7 +623,6 @@
   loadmodule("lualibs-io.lua")
   loadmodule("lualibs-os.lua")
   loadmodule("lualibs-file.lua")
-  loadmodule("lualibs-gzip.lua")
   loadmodule("lualibs-md5.lua")
   loadmodule("lualibs-dir.lua")
   loadmodule("lualibs-unicode.lua")
@@ -664,8 +665,8 @@
 
 local lualibs_extended_module = {
   name          = "lualibs-extended",
-  version       = "2.74",       --TAGVERSION
-  date          = "2021-05-20", --TAGDATE
+  version       = "2.75",       --TAGVERSION
+  date          = "2022-10-04", --TAGDATE
   description   = "ConTeXt Lua libraries -- extended collection.",
   author        = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang",
   copyright     = "PRAGMA ADE / ConTeXt Development Team",
@@ -776,6 +777,7 @@
 end
 
 if loaded == false then
+  loadmodule("lualibs-util-sac.lua")--- streams: string based file parsers
   loadmodule("lualibs-util-str.lua")--- string formatters (fast)
   loadmodule("lualibs-util-fil.lua")--- extra file helpers
   loadmodule("lualibs-util-tab.lua")--- extended table operations
@@ -795,6 +797,7 @@
   loadmodule("lualibs-util-deb.lua")--- extra debugging
   loadmodule("lualibs-util-tpl.lua")--- templating
   loadmodule("lualibs-util-sta.lua")--- stacker (for writing pdf)
+  loadmodule("lualibs-util-zip.lua")--- compression and zip files
 end
 
 unfake_context() --- TODO check if this works at runtime

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -1,6 +1,6 @@
 -- merged file : lualibs-basic-merged.lua
 -- parent file : lualibs-basic.lua
--- merge date  : 2021-05-20 23:14
+-- merge date  : 2022-10-04 17:16
 
 do -- begin closure to overcome local limits and interference
 
@@ -2737,8 +2737,14 @@
  math.ceiling=math.ceil
 end
 if not math.round then
- local floor=math.floor
- function math.round(x) return floor(x+0.5) end
+ if xmath then
+  math.round=xmath.round
+ else
+  local floor=math.floor
+  function math.round(x)
+   return x<0 and -floor(-x+0.5) or floor(x+0.5)
+  end
+ end
 end
 if not math.div then
  local floor=math.floor
@@ -2808,7 +2814,7 @@
 end
 if not math.ult then
  local floor=math.floor
- function math.tointeger(m,n)
+ function math.ult(m,n)
   return floor(m)<floor(n) 
  end
 end
@@ -2882,9 +2888,12 @@
   flush()
  end
 end
-function io.savedata(filename,data,joiner)
- local f=open(filename,"wb")
+function io.savedata(filename,data,joiner,append)
+ local f=open(filename,append and "ab" or "wb")
  if f then
+  if append and joiner and f:seek("end")>0 then
+   f:write(joiner)
+  end
   if type(data)=="table" then
    f:write(concat(data,joiner or ""))
   elseif type(data)=="function" then
@@ -3172,10 +3181,10 @@
  license="see context related readme files"
 }
 local os=os
-local date,time=os.date,os.time
+local date,time,difftime=os.date,os.time,os.difftime
 local find,format,gsub,upper,gmatch=string.find,string.format,string.gsub,string.upper,string.gmatch
 local concat=table.concat
-local random,ceil,randomseed=math.random,math.ceil,math.randomseed
+local random,ceil,randomseed,modf=math.random,math.ceil,math.randomseed,math.modf
 local type,setmetatable,tonumber,tostring=type,setmetatable,tonumber,tostring
 do
  local selfdir=os.selfdir
@@ -3273,7 +3282,7 @@
    osenv[K]=v
   end
   function os.getenv(k)
-   local K=upper(k)
+   local K=upper(k) 
    local v=osenv[K] or osgetenv(K) or osgetenv(k)
    if v=="" then
     return nil
@@ -3291,22 +3300,6 @@
   setmetatable(os.env,{ __index=__index,__newindex=__newindex } )
  end
 end
-local execute=os.execute
-local iopopen=io.popen
-local function resultof(command)
- local handle=iopopen(command,"r") 
- if handle then
-  local result=handle:read("*all") or ""
-  handle:close()
-  return result
- else
-  return ""
- end
-end
-os.resultof=resultof
-function os.pipeto(command)
- return iopopen(command,"w") 
-end
 if not io.fileseparator then
  if find(os.getenv("PATH"),";",1,true) then
   io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "windows"
@@ -3321,229 +3314,232 @@
 else
  os.libsuffix,os.binsuffix,os.binsuffixes='so','',{ '' }
 end
-local launchers={
- windows="start %s",
- macosx="open %s",
- unix="xdg-open %s &> /dev/null &",
-}
-function os.launch(str)
- local command=format(launchers[os.name] or launchers.unix,str)
- execute(command)
+do
+ local execute=os.execute
+ local iopopen=io.popen
+ local ostype=os.type
+ local function resultof(command)
+  local handle=iopopen(command,ostype=="windows" and "rb" or "r")
+  if handle then
+   local result=handle:read("*all") or ""
+   handle:close()
+   return result
+  else
+   return ""
+  end
+ end
+ os.resultof=resultof
+ function os.pipeto(command)
+  return iopopen(command,"w") 
+ end
+ local launchers={
+  windows="start %s",
+  macosx="open %s",
+  unix="xdg-open %s &> /dev/null &",
+ }
+ function os.launch(str)
+  local command=format(launchers[os.name] or launchers.unix,str)
+  execute(command)
+ end
 end
-local gettimeofday=os.gettimeofday or os.clock
-os.gettimeofday=gettimeofday
-local startuptime=gettimeofday()
-function os.runtime()
- return gettimeofday()-startuptime
+do
+ local gettimeofday=os.gettimeofday or os.clock
+ os.gettimeofday=gettimeofday
+ local startuptime=gettimeofday()
+ function os.runtime()
+  return gettimeofday()-startuptime
+ end
 end
-local resolvers=os.resolvers or {}
-os.resolvers=resolvers
-setmetatable(os,{ __index=function(t,k)
- local r=resolvers[k]
- return r and r(t,k) or nil 
-end })
-local name,platform=os.name or "linux",os.getenv("MTX_PLATFORM") or ""
-if platform~="" then
- os.platform=platform
-elseif os.type=="windows" then
- function resolvers.platform(t,k)
-  local architecture=os.getenv("PROCESSOR_ARCHITECTURE") or ""
-  local platform=""
-  if find(architecture,"AMD64",1,true) then
-   platform="win64"
+do
+ local name=os.name or "linux"
+ local platform=os.getenv("MTX_PLATFORM") or ""
+ local architecture=os.uname and os.uname().machine 
+ local bits=os.getenv("MTX_BITS") or find(platform,"64") and 64 or 32
+ if platform~="" then
+ elseif os.type=="windows" then
+  architecture=string.lower(architecture or os.getenv("PROCESSOR_ARCHITECTURE") or "")
+  if architecture=="x86_64" then
+   bits,platform=64,"win64"
+  elseif find(architecture,"amd64") then
+   bits,platform=64,"win64"
+  elseif find(architecture,"arm64") then
+   bits,platform=64,"windows-arm64"
+  elseif find(architecture,"arm32") then
+   bits,platform=32,"windows-arm32"
   else
-   platform="mswin"
+   bits,platform=32,"mswin"
   end
-  os.setenv("MTX_PLATFORM",platform)
-  os.platform=platform
-  return platform
- end
-elseif name=="linux" then
- function resolvers.platform(t,k)
-  local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or ""
-  local platform=os.getenv("MTX_PLATFORM") or ""
+ elseif name=="linux" then
+  architecture=architecture or os.getenv("HOSTTYPE") or resultof("uname -m") or ""
   local musl=find(os.selfdir or "","linuxmusl")
-  if platform~="" then
-  elseif find(architecture,"x86_64",1,true) then
-   platform=musl and "linuxmusl" or "linux-64"
-  elseif find(architecture,"ppc",1,true) then
-   platform="linux-ppc"
+  if find(architecture,"x86_64") then
+   bits,platform=64,musl and "linuxmusl" or "linux-64"
+  elseif find(architecture,"ppc") then
+   bits,platform=32,"linux-ppc" 
   else
-   platform=musl and "linuxmusl" or "linux"
+   bits,platform=32,musl and "linuxmusl" or "linux"
   end
-  os.setenv("MTX_PLATFORM",platform)
-  os.platform=platform
-  return platform
- end
-elseif name=="macosx" then
- function resolvers.platform(t,k)
-  local architecture=resultof("echo $HOSTTYPE") or ""
-  local platform=""
+ elseif name=="macosx" then
+  architecture=architecture or resultof("echo $HOSTTYPE") or ""
   if architecture=="" then
-   platform="osx-intel"
-  elseif find(architecture,"i386",1,true) then
-   platform="osx-intel"
-  elseif find(architecture,"x86_64",1,true) then
-   platform="osx-64"
-  elseif find(architecture,"arm64",1,true) then
-   platform="osx-arm"
+   bits,platform=64,"osx-intel"
+  elseif find(architecture,"i386") then
+   bits,platform=64,"osx-intel"
+  elseif find(architecture,"x86_64") then
+   bits,platform=64,"osx-64"
+  elseif find(architecture,"arm64") then
+   bits,platform=64,"osx-arm"
   else
-   platform="osx-ppc"
+   bits,platform=32,"osx-ppc"
   end
-  os.setenv("MTX_PLATFORM",platform)
-  os.platform=platform
-  return platform
- end
-elseif name=="sunos" then
- function resolvers.platform(t,k)
-  local architecture=resultof("uname -m") or ""
-  local platform=""
-  if find(architecture,"sparc",1,true) then
-   platform="solaris-sparc"
+ elseif name=="sunos" then
+  architecture=architecture or resultof("uname -m") or ""
+  if find(architecture,"sparc") then
+   bits,platform=32,"solaris-sparc"
   else 
-   platform="solaris-intel"
+   bits,platform=32,"solaris-intel"
   end
-  os.setenv("MTX_PLATFORM",platform)
-  os.platform=platform
-  return platform
- end
-elseif name=="freebsd" then
- function resolvers.platform(t,k)
-  local architecture=resultof("uname -m") or ""
-  local platform=""
-  if find(architecture,"amd64",1,true) then
-   platform="freebsd-amd64"
+ elseif name=="freebsd" then
+  architecture=architecture or os.getenv("MACHTYPE") or resultof("uname -m") or ""
+  if find(architecture,"amd64") or find(architecture,"AMD64") then
+   bits,platform=64,"freebsd-amd64"
   else
-   platform="freebsd"
+   bits,platform=32,"freebsd"
   end
-  os.setenv("MTX_PLATFORM",platform)
-  os.platform=platform
-  return platform
- end
-elseif name=="kfreebsd" then
- function resolvers.platform(t,k)
-  local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or ""
-  local platform=""
-  if find(architecture,"x86_64",1,true) then
-   platform="kfreebsd-amd64"
+ elseif name=="kfreebsd" then
+  architecture=architecture or os.getenv("HOSTTYPE") or resultof("uname -m") or ""
+  if architecture=="x86_64" then
+   bits,platform=64,"kfreebsd-amd64"
   else
-   platform="kfreebsd-i386"
+   bits,platform=32,"kfreebsd-i386"
   end
-  os.setenv("MTX_PLATFORM",platform)
-  os.platform=platform
-  return platform
+ else
+  architecture=architecture or resultof("uname -m") or ""
+  if find(architecture,"aarch64") then
+   bits,platform="linux-aarch64"
+  elseif find(architecture,"armv7l") then
+   bits,platform=32,"linux-armhf"
+  elseif find(architecture,"mips64") or find(architecture,"mips64el") then
+   bits,platform=64,"linux-mipsel"
+  elseif find(architecture,"mipsel") or find(architecture,"mips") then
+   bits,platform=32,"linux-mipsel"
+  else
+   bits,platform=64,"linux-64" 
+  end
  end
-else
- function resolvers.platform(t,k)
-  local platform="linux"
-  os.setenv("MTX_PLATFORM",platform)
-  os.platform=platform
-  return platform
- end
-end
-os.newline=name=="windows" and "\013\010" or "\010" 
-function resolvers.bits(t,k)
- local bits=find(os.platform,"64",1,true) and 64 or 32
+ os.setenv("MTX_PLATFORM",platform)
+ os.setenv("MTX_BITS",bits)
+ os.platform=platform
  os.bits=bits
- return bits
+ os.newline=name=="windows" and "\013\010" or "\010" 
 end
-local t={ 8,9,"a","b" }
-function os.uuid()
- return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
-  random(0xFFFF),random(0xFFFF),
-  random(0x0FFF),
-  t[ceil(random(4))] or 8,random(0x0FFF),
-  random(0xFFFF),
-  random(0xFFFF),random(0xFFFF),random(0xFFFF)
- )
+do
+ local t={ 8,9,"a","b" }
+ function os.uuid()
+  return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
+   random(0xFFFF),random(0xFFFF),
+   random(0x0FFF),
+   t[ceil(random(4))] or 8,random(0x0FFF),
+   random(0xFFFF),
+   random(0xFFFF),random(0xFFFF),random(0xFFFF)
+  )
+ end
 end
-local d
-function os.timezone(delta)
- d=d or ((tonumber(date("%H")) or 0)-(tonumber(date("!%H")) or 0))
- if delta then
-  if d>0 then
-   return format("+%02i:00",d)
+do
+ local hour,min
+ function os.timezone(difference)
+  if not hour then
+   local current=time()
+   local utcdate=date("!*t",current)
+   local localdate=date("*t",current)
+   localdate.isdst=false
+   local timediff=difftime(time(localdate),time(utcdate))
+   hour,min=modf(timediff/3600)
+   min=min*60
+  end
+  if difference then
+   return hour,min
   else
-   return format("-%02i:00",-d)
+   return format("%+03d:%02d",hour,min) 
   end
- else
-  return 1
  end
-end
-local timeformat=format("%%s%s",os.timezone(true))
-local dateformat="!%Y-%m-%d %H:%M:%S"
-local lasttime=nil
-local lastdate=nil
-function os.fulltime(t,default)
- t=t and tonumber(t) or 0
- if t>0 then
- elseif default then
-  return default
- else
-  t=time()
+ local timeformat=format("%%s%s",os.timezone())
+ local dateformat="%Y-%m-%d %H:%M:%S"
+ local lasttime=nil
+ local lastdate=nil
+ function os.fulltime(t,default)
+  t=t and tonumber(t) or 0
+  if t>0 then
+  elseif default then
+   return default
+  else
+   t=time()
+  end
+  if t~=lasttime then
+   lasttime=t
+   lastdate=format(timeformat,date(dateformat))
+  end
+  return lastdate
  end
- if t~=lasttime then
-  lasttime=t
-  lastdate=format(timeformat,date(dateformat))
+ local dateformat="%Y-%m-%d %H:%M:%S"
+ local lasttime=nil
+ local lastdate=nil
+ function os.localtime(t,default)
+  t=t and tonumber(t) or 0
+  if t>0 then
+  elseif default then
+   return default
+  else
+   t=time()
+  end
+  if t~=lasttime then
+   lasttime=t
+   lastdate=date(dateformat,t)
+  end
+  return lastdate
  end
- return lastdate
-end
-local dateformat="%Y-%m-%d %H:%M:%S"
-local lasttime=nil
-local lastdate=nil
-function os.localtime(t,default)
- t=t and tonumber(t) or 0
- if t>0 then
- elseif default then
-  return default
- else
-  t=time()
+ function os.converttime(t,default)
+  local t=tonumber(t)
+  if t and t>0 then
+   return date(dateformat,t)
+  else
+   return default or "-"
+  end
  end
- if t~=lasttime then
-  lasttime=t
-  lastdate=date(dateformat,t)
+ function os.today()
+  return date("!*t")
  end
- return lastdate
-end
-function os.converttime(t,default)
- local t=tonumber(t)
- if t and t>0 then
-  return date(dateformat,t)
- else
-  return default or "-"
+ function os.now()
+  return date("!%Y-%m-%d %H:%M:%S")
  end
 end
-local memory={}
-local function which(filename)
- local fullname=memory[filename]
- if fullname==nil then
-  local suffix=file.suffix(filename)
-  local suffixes=suffix=="" and os.binsuffixes or { suffix }
-  for directory in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
-   local df=file.join(directory,filename)
-   for i=1,#suffixes do
-    local dfs=file.addsuffix(df,suffixes[i])
-    if io.exists(dfs) then
-     fullname=dfs
-     break
+do
+ local cache={}
+ local function which(filename)
+  local fullname=cache[filename]
+  if fullname==nil then
+   local suffix=file.suffix(filename)
+   local suffixes=suffix=="" and os.binsuffixes or { suffix }
+   for directory in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do
+    local df=file.join(directory,filename)
+    for i=1,#suffixes do
+     local dfs=file.addsuffix(df,suffixes[i])
+     if io.exists(dfs) then
+      fullname=dfs
+      break
+     end
     end
    end
+   if not fullname then
+    fullname=false
+   end
+   cache[filename]=fullname
   end
-  if not fullname then
-   fullname=false
-  end
-  memory[filename]=fullname
+  return fullname
  end
- return fullname
+ os.which=which
+ os.where=which
 end
-os.which=which
-os.where=which
-function os.today()
- return date("!*t") 
-end
-function os.now()
- return date("!%Y-%m-%d %H:%M:%S") 
-end
 if not os.sleep then
  local socket=socket
  function os.sleep(n)
@@ -3553,65 +3549,69 @@
   socket.sleep(n)
  end
 end
-local function isleapyear(year)
- return (year%4==0) and (year%100~=0 or year%400==0)
-end
-os.isleapyear=isleapyear
-local days={ 31,28,31,30,31,30,31,31,30,31,30,31 }
-local function nofdays(year,month,day)
- if not month then
-  return isleapyear(year) and 365 or 364
- elseif not day then
-  return month==2 and isleapyear(year) and 29 or days[month]
- else
-  for i=1,month-1 do
-   day=day+days[i]
+do
+ local function isleapyear(year)
+  return (year%4==0) and (year%100~=0 or year%400==0)
+ end
+ os.isleapyear=isleapyear
+ local days={ 31,28,31,30,31,30,31,31,30,31,30,31 }
+ local function nofdays(year,month,day)
+  if not month then
+   return isleapyear(year) and 365 or 364
+  elseif not day then
+   return month==2 and isleapyear(year) and 29 or days[month]
+  else
+   for i=1,month-1 do
+    day=day+days[i]
+   end
+   if month>2 and isleapyear(year) then
+    day=day+1
+   end
+   return day
   end
-  if month>2 and isleapyear(year) then
-   day=day+1
-  end
-  return day
  end
-end
-os.nofdays=nofdays
-function os.weekday(day,month,year)
- return date("%w",time { year=year,month=month,day=day })+1
-end
-function os.validdate(year,month,day)
- if month<1 then
-  month=1
- elseif month>12 then
-  month=12
+ os.nofdays=nofdays
+ function os.weekday(day,month,year)
+  return date("%w",time { year=year,month=month,day=day })+1
  end
- if day<1 then
-  day=1
- else
-  local max=nofdays(year,month)
-  if day>max then
-   day=max
+ function os.validdate(year,month,day)
+  if month<1 then
+   month=1
+  elseif month>12 then
+   month=12
   end
+  if day<1 then
+   day=1
+  else
+   local max=nofdays(year,month)
+   if day>max then
+    day=max
+   end
+  end
+  return year,month,day
  end
- return year,month,day
-end
-function os.date(fmt,...)
- if not fmt then
-  fmt="%Y-%m-%d %H:%M"
+ function os.date(fmt,...)
+  if not fmt then
+   fmt="%Y-%m-%d %H:%M"
+  end
+  return date(fmt,...)
  end
- return date(fmt,...)
 end
-local osexit=os.exit
-local exitcode=nil
-function os.setexitcode(code)
- exitcode=code
-end
-function os.exit(c)
- if exitcode~=nil then
-  return osexit(exitcode)
+do
+ local osexit=os.exit
+ local exitcode=nil
+ function os.setexitcode(code)
+  exitcode=code
  end
- if c~=nil then
-  return osexit(c)
+ function os.exit(c)
+  if exitcode~=nil then
+   return osexit(exitcode)
+  end
+  if c~=nil then
+   return osexit(c)
+  end
+  return osexit()
  end
- return osexit()
 end
 
 end -- closure
@@ -3872,7 +3872,7 @@
  if not two then
   return one=="" and one or lpegmatch(reslasher,one)
  end
- if one=="" then
+ if not one or one=="" then
   return lpegmatch(stripper,three and concat({ two,three,... },"/") or two)
  end
  if lpegmatch(isnetwork,one) then
@@ -4031,87 +4031,6 @@
 
 do -- begin closure to overcome local limits and interference
 
-if not modules then modules={} end modules ['l-gzip']={
- version=1.001,
- author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright="PRAGMA ADE / ConTeXt Development Team",
- license="see context related readme files"
-}
-gzip=gzip or {} 
-if not zlib then
- zlib=xzip 
-elseif not xzip then
- xzip=zlib
-end
-if zlib then
- local suffix=file.suffix
- local suffixes=file.suffixes
- local find=string.find
- local openfile=io.open
- local gzipwindow=15+16 
- local gziplevel=3
- local identifier="^\x1F\x8B\x08"
- local compress=zlib.compress
- local decompress=zlib.decompress
- function gzip.load(filename)
-  local f=openfile(filename,"rb")
-  if not f then
-  else
-   local data=f:read("*all")
-   f:close()
-   if data and data~="" then
-    if suffix(filename)=="gz" then
-     data=decompress(data,gzipwindow)
-    end
-    return data
-   end
-  end
- end
- function gzip.save(filename,data,level)
-  if suffix(filename)~="gz" then
-   filename=filename..".gz"
-  end
-  local f=openfile(filename,"wb")
-  if f then
-   data=compress(data or "",level or gziplevel,nil,gzipwindow)
-   f:write(data)
-   f:close()
-   return #data
-  end
- end
- function gzip.suffix(filename)
-  local suffix,extra=suffixes(filename)
-  local gzipped=extra=="gz"
-  return suffix,gzipped
- end
- function gzip.compressed(s)
-  return s and find(s,identifier)
- end
- function gzip.compress(s,level)
-  if s and not find(s,identifier) then 
-   if not level then
-    level=gziplevel
-   elseif level<=0 then
-    return s
-   elseif level>9 then
-    level=9
-   end
-   return compress(s,level or gziplevel,nil,gzipwindow) or s
-  end
- end
- function gzip.decompress(s)
-  if s and find(s,identifier) then
-   return decompress(s,gzipwindow)
-  else
-   return s
-  end
- end
-end
-
-end -- closure
-
-do -- begin closure to overcome local limits and interference
-
 if not modules then modules={} end modules ['l-md5']={
  version=1.001,
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
@@ -4389,15 +4308,15 @@
 if onwindows then 
  local slash=S("/\\")/"/"
  pattern={
-  [1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3),
-  [2]=Cs(((1-S("*?/\\"))^0*slash)^0),
-  [3]=Cs(P(1)^0)
+  (Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3),
+  Cs(((1-S("*?/\\"))^0*slash)^0),
+  Cs(P(1)^0)
  }
 else
  pattern={
-  [1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3),
-  [2]=C(((1-S("*?/"))^0*P("/"))^0),
-  [3]=C(P(1)^0)
+  (C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3),
+  C(((1-S("*?/"))^0*P("/"))^0),
+  C(P(1)^0)
  }
 end
 local filter=Cs ((

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -29,8 +29,8 @@
 
 local lualibs_basic_module = {
   name          = "lualibs-basic",
-  version       = "2.74",       --TAGVERSION
-  date          = "2021-05-20", --TAGDATE
+  version       = "2.75",       --TAGVERSION
+  date          = "2022-10-04", --TAGDATE
   description   = "ConTeXt Lua libraries -- basic collection.",
   author        = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang",
   copyright     = "PRAGMA ADE / ConTeXt Development Team",
@@ -61,7 +61,6 @@
   loadmodule("lualibs-io.lua")
   loadmodule("lualibs-os.lua")
   loadmodule("lualibs-file.lua")
-  loadmodule("lualibs-gzip.lua")
   loadmodule("lualibs-md5.lua")
   loadmodule("lualibs-dir.lua")
   loadmodule("lualibs-unicode.lua")

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -230,9 +230,9 @@
 
 --     pattern = Ct {
     pattern = {
-        [1] = (Cs(P(".") + slash^1) + Cs(R("az","AZ") * P(":") * slash^0) + Cc("./")) * V(2) * V(3),
-        [2] = Cs(((1-S("*?/\\"))^0 * slash)^0),
-        [3] = Cs(P(1)^0)
+        (Cs(P(".") + slash^1) + Cs(R("az","AZ") * P(":") * slash^0) + Cc("./")) * V(2) * V(3),
+        Cs(((1-S("*?/\\"))^0 * slash)^0),
+        Cs(P(1)^0)
     }
 
 else -- assume unix
@@ -239,9 +239,9 @@
 
 --     pattern = Ct {
     pattern = {
-        [1] = (C(P(".") + P("/")^1) + Cc("./")) * V(2) * V(3),
-        [2] = C(((1-S("*?/"))^0 * P("/"))^0),
-        [3] = C(P(1)^0)
+        (C(P(".") + P("/")^1) + Cc("./")) * V(2) * V(3),
+        C(((1-S("*?/"))^0 * P("/"))^0),
+        C(P(1)^0)
     }
 
 end

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended-merged.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended-merged.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended-merged.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -1,9 +1,514 @@
 -- merged file : lualibs-extended-merged.lua
 -- parent file : lualibs-extended.lua
--- merge date  : 2021-05-20 23:14
+-- merge date  : 2022-10-04 17:16
 
 do -- begin closure to overcome local limits and interference
 
+if not modules then modules={} end modules ['util-sac']={
+ version=1.001,
+ optimize=true,
+ comment="companion to luat-lib.mkiv",
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local byte,sub=string.byte,string.sub
+local tonumber=tonumber
+utilities=utilities or {}
+local streams={}
+utilities.streams=streams
+function streams.open(filename,zerobased)
+ local f=filename and io.loaddata(filename)
+ if f then
+  return { f,1,#f,zerobased or false }
+ end
+end
+function streams.openstring(f,zerobased)
+ if f then
+  return { f,1,#f,zerobased or false }
+ end
+end
+function streams.getstring(f)
+ if f then
+  return f[1]
+ end
+end
+function streams.close()
+end
+function streams.size(f)
+ return f and f[3] or 0
+end
+streams.getsize=streams.size
+function streams.setposition(f,i)
+ if f[4] then
+  if i<=0 then
+   f[2]=1
+  else
+   f[2]=i+1
+  end
+ else
+  if i<=1 then
+   f[2]=1
+  else
+   f[2]=i
+  end
+ end
+end
+function streams.getposition(f)
+ if f[4] then
+  return f[2]-1
+ else
+  return f[2]
+ end
+end
+function streams.look(f,n,chars)
+ local b=f[2]
+ local e=b+n-1
+ if chars then
+  return sub(f[1],b,e)
+ else
+  return byte(f[1],b,e)
+ end
+end
+function streams.skip(f,n)
+ f[2]=f[2]+n
+end
+function streams.readbyte(f)
+ local i=f[2]
+ f[2]=i+1
+ return byte(f[1],i)
+end
+function streams.readbytes(f,n)
+ local i=f[2]
+ local j=i+n
+ f[2]=j
+ return byte(f[1],i,j-1)
+end
+function streams.readbytetable(f,n)
+ local i=f[2]
+ local j=i+n
+ f[2]=j
+ return { byte(f[1],i,j-1) }
+end
+function streams.skipbytes(f,n)
+ f[2]=f[2]+n
+end
+function streams.readchar(f)
+ local i=f[2]
+ f[2]=i+1
+ return sub(f[1],i,i)
+end
+function streams.readstring(f,n)
+ local i=f[2]
+ local j=i+n
+ f[2]=j
+ return sub(f[1],i,j-1)
+end
+function streams.readinteger1(f)  
+ local i=f[2]
+ f[2]=i+1
+ local n=byte(f[1],i)
+ if n>=0x80 then
+  return n-0x100
+ else
+  return n
+ end
+end
+streams.readcardinal1=streams.readbyte  
+streams.readcardinal=streams.readcardinal1
+streams.readinteger=streams.readinteger1
+function streams.readcardinal2(f)
+ local i=f[2]
+ local j=i+1
+ f[2]=j+1
+ local a,b=byte(f[1],i,j)
+ return 0x100*a+b
+end
+function streams.readcardinal2le(f)
+ local i=f[2]
+ local j=i+1
+ f[2]=j+1
+ local b,a=byte(f[1],i,j)
+ return 0x100*a+b
+end
+function streams.readinteger2(f)
+ local i=f[2]
+ local j=i+1
+ f[2]=j+1
+ local a,b=byte(f[1],i,j)
+ if a>=0x80 then
+  return 0x100*a+b-0x10000
+ else
+  return 0x100*a+b
+ end
+end
+function streams.readinteger2le(f)
+ local i=f[2]
+ local j=i+1
+ f[2]=j+1
+ local b,a=byte(f[1],i,j)
+ if a>=0x80 then
+  return 0x100*a+b-0x10000
+ else
+  return 0x100*a+b
+ end
+end
+function streams.readcardinal3(f)
+ local i=f[2]
+ local j=i+2
+ f[2]=j+1
+ local a,b,c=byte(f[1],i,j)
+ return 0x10000*a+0x100*b+c
+end
+function streams.readcardinal3le(f)
+ local i=f[2]
+ local j=i+2
+ f[2]=j+1
+ local c,b,a=byte(f[1],i,j)
+ return 0x10000*a+0x100*b+c
+end
+function streams.readinteger3(f)
+ local i=f[2]
+ local j=i+3
+ f[2]=j+1
+ local a,b,c=byte(f[1],i,j)
+ if a>=0x80 then
+  return 0x10000*a+0x100*b+c-0x1000000
+ else
+  return 0x10000*a+0x100*b+c
+ end
+end
+function streams.readinteger3le(f)
+ local i=f[2]
+ local j=i+3
+ f[2]=j+1
+ local c,b,a=byte(f[1],i,j)
+ if a>=0x80 then
+  return 0x10000*a+0x100*b+c-0x1000000
+ else
+  return 0x10000*a+0x100*b+c
+ end
+end
+function streams.readcardinal4(f)
+ local i=f[2]
+ local j=i+3
+ f[2]=j+1
+ local a,b,c,d=byte(f[1],i,j)
+ return 0x1000000*a+0x10000*b+0x100*c+d
+end
+function streams.readcardinal4le(f)
+ local i=f[2]
+ local j=i+3
+ f[2]=j+1
+ local d,c,b,a=byte(f[1],i,j)
+ return 0x1000000*a+0x10000*b+0x100*c+d
+end
+function streams.readinteger4(f)
+ local i=f[2]
+ local j=i+3
+ f[2]=j+1
+ local a,b,c,d=byte(f[1],i,j)
+ if a>=0x80 then
+  return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
+ else
+  return 0x1000000*a+0x10000*b+0x100*c+d
+ end
+end
+function streams.readinteger4le(f)
+ local i=f[2]
+ local j=i+3
+ f[2]=j+1
+ local d,c,b,a=byte(f[1],i,j)
+ if a>=0x80 then
+  return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000
+ else
+  return 0x1000000*a+0x10000*b+0x100*c+d
+ end
+end
+function streams.readfixed2(f)
+ local i=f[2]
+ local j=i+1
+ f[2]=j+1
+ local n1,n2=byte(f[1],i,j)
+ if n1>=0x80 then
+  n1=n1-0x100
+ end
+ return n1+n2/0xFF
+end
+function streams.readfixed4(f)
+ local i=f[2]
+ local j=i+3
+ f[2]=j+1
+ local a,b,c,d=byte(f[1],i,j)
+ local n1=0x100*a+b
+ local n2=0x100*c+d
+ if n1>=0x8000 then
+  n1=n1-0x10000
+ end
+ return n1+n2/0xFFFF
+end
+if bit32 then
+ local extract=bit32.extract
+ local band=bit32.band
+ function streams.read2dot14(f)
+  local i=f[2]
+  local j=i+1
+  f[2]=j+1
+  local a,b=byte(f[1],i,j)
+  if a>=0x80 then
+   local n=-(0x100*a+b)
+   return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+  else
+   local n=0x100*a+b
+   return   (extract(n,14,2)+(band(n,0x3FFF)/16384.0))
+  end
+ end
+end
+function streams.skipshort(f,n)
+ f[2]=f[2]+2*(n or 1)
+end
+function streams.skiplong(f,n)
+ f[2]=f[2]+4*(n or 1)
+end
+if sio and sio.readcardinal2 then
+ local readcardinal1=sio.readcardinal1
+ local readcardinal2=sio.readcardinal2
+ local readcardinal3=sio.readcardinal3
+ local readcardinal4=sio.readcardinal4
+ local readinteger1=sio.readinteger1
+ local readinteger2=sio.readinteger2
+ local readinteger3=sio.readinteger3
+ local readinteger4=sio.readinteger4
+ local readfixed2=sio.readfixed2
+ local readfixed4=sio.readfixed4
+ local read2dot14=sio.read2dot14
+ local readbytes=sio.readbytes
+ local readbytetable=sio.readbytetable
+ function streams.readcardinal1(f)
+  local i=f[2]
+  f[2]=i+1
+  return readcardinal1(f[1],i)
+ end
+ function streams.readcardinal2(f)
+  local i=f[2]
+  f[2]=i+2
+  return readcardinal2(f[1],i)
+ end
+ function streams.readcardinal3(f)
+  local i=f[2]
+  f[2]=i+3
+  return readcardinal3(f[1],i)
+ end
+ function streams.readcardinal4(f)
+  local i=f[2]
+  f[2]=i+4
+  return readcardinal4(f[1],i)
+ end
+ function streams.readinteger1(f)
+  local i=f[2]
+  f[2]=i+1
+  return readinteger1(f[1],i)
+ end
+ function streams.readinteger2(f)
+  local i=f[2]
+  f[2]=i+2
+  return readinteger2(f[1],i)
+ end
+ function streams.readinteger3(f)
+  local i=f[2]
+  f[2]=i+3
+  return readinteger3(f[1],i)
+ end
+ function streams.readinteger4(f)
+  local i=f[2]
+  f[2]=i+4
+  return readinteger4(f[1],i)
+ end
+ function streams.readfixed2(f) 
+  local i=f[2]
+  f[2]=i+2
+  return readfixed2(f[1],i)
+ end
+ function streams.readfixed4(f) 
+  local i=f[2]
+  f[2]=i+4
+  return readfixed4(f[1],i)
+ end
+ function streams.read2dot14(f)
+  local i=f[2]
+  f[2]=i+2
+  return read2dot14(f[1],i)
+ end
+ function streams.readbytes(f,n)
+  local i=f[2]
+  local s=f[3]
+  local p=i+n
+  if p>s then
+   f[2]=s+1
+  else
+   f[2]=p
+  end
+  return readbytes(f[1],i,n)
+ end
+ function streams.readbytetable(f,n)
+  local i=f[2]
+  local s=f[3]
+  local p=i+n
+  if p>s then
+   f[2]=s+1
+  else
+   f[2]=p
+  end
+  return readbytetable(f[1],i,n)
+ end
+ streams.readbyte=streams.readcardinal1
+ streams.readsignedbyte=streams.readinteger1
+ streams.readcardinal=streams.readcardinal1
+ streams.readinteger=streams.readinteger1
+end
+if sio and sio.readcardinaltable then
+ local readcardinaltable=sio.readcardinaltable
+ local readintegertable=sio.readintegertable
+ function utilities.streams.readcardinaltable(f,n,b)
+  local i=f[2]
+  local s=f[3]
+  local p=i+n*b
+  if p>s then
+   f[2]=s+1
+  else
+   f[2]=p
+  end
+  return readcardinaltable(f[1],i,n,b)
+ end
+ function utilities.streams.readintegertable(f,n,b)
+  local i=f[2]
+  local s=f[3]
+  local p=i+n*b
+  if p>s then
+   f[2]=s+1
+  else
+   f[2]=p
+  end
+  return readintegertable(f[1],i,n,b)
+ end
+else
+ local readcardinal1=streams.readcardinal1
+ local readcardinal2=streams.readcardinal2
+ local readcardinal3=streams.readcardinal3
+ local readcardinal4=streams.readcardinal4
+ function streams.readcardinaltable(f,n,b)
+  local i=f[2]
+  local s=f[3]
+  local p=i+n*b
+  if p>s then
+   f[2]=s+1
+  else
+   f[2]=p
+  end
+  local t={}
+   if b==1 then for i=1,n do t[i]=readcardinal1(f[1],i) end
+  elseif b==2 then for i=1,n do t[i]=readcardinal2(f[1],i) end
+  elseif b==3 then for i=1,n do t[i]=readcardinal3(f[1],i) end
+  elseif b==4 then for i=1,n do t[i]=readcardinal4(f[1],i) end end
+  return t
+ end
+ local readinteger1=streams.readinteger1
+ local readinteger2=streams.readinteger2
+ local readinteger3=streams.readinteger3
+ local readinteger4=streams.readinteger4
+ function streams.readintegertable(f,n,b)
+  local i=f[2]
+  local s=f[3]
+  local p=i+n*b
+  if p>s then
+   f[2]=s+1
+  else
+   f[2]=p
+  end
+  local t={}
+   if b==1 then for i=1,n do t[i]=readinteger1(f[1],i) end
+  elseif b==2 then for i=1,n do t[i]=readinteger2(f[1],i) end
+  elseif b==3 then for i=1,n do t[i]=readinteger3(f[1],i) end
+  elseif b==4 then for i=1,n do t[i]=readinteger4(f[1],i) end end
+  return t
+ end
+end
+do
+ local files=utilities.files
+ if files then
+  local openfile=files.open
+  local openstream=streams.open
+  local openstring=streams.openstring
+  local setmetatable=setmetatable
+  function io.newreader(str,method)
+   local f,m
+   if method=="string" then
+    f=openstring(str,true)
+    m=streams
+   elseif method=="stream" then
+    f=openstream(str,true)
+    m=streams
+   else
+    f=openfile(str,"rb")
+    m=files
+   end
+   if f then
+    local t={}
+    setmetatable(t,{
+     __index=function(t,k)
+      local r=m[k]
+      if k=="close" then
+       if f then
+        m.close(f)
+        f=nil
+       end
+       return function() end
+      elseif r then
+       local v=function(_,a,b) return r(f,a,b) end
+       t[k]=v
+       return v
+      else
+       print("unknown key",k)
+      end
+     end
+    } )
+    return t
+   end
+  end
+ end
+end
+if bit32 and not streams.tocardinal1 then
+ local extract=bit32.extract
+ local char=string.char
+    streams.tocardinal1=char
+ function streams.tocardinal2(n)   return char(extract(8,8),extract(0,8)) end
+ function streams.tocardinal3(n)   return char(extract(16,8),extract(8,8),extract(0,8)) end
+ function streams.tocardinal4(n)   return char(extract(24,8),extract(16,8),extract(8,8),extract(0,8)) end
+    streams.tocardinal1le=char
+ function streams.tocardinal2le(n) return char(extract(0,8),extract(8,8)) end
+ function streams.tocardinal3le(n) return char(extract(0,8),extract(8,8),extract(16,8)) end
+ function streams.tocardinal4le(n) return char(extract(0,8),extract(8,8),extract(16,8),extract(24,8)) end
+end
+if not streams.readcstring then
+ local readchar=streams.readchar
+ local concat=table.concat
+ function streams.readcstring(f)
+  local t={}
+  while true do
+   local c=readchar(f)
+   if c and c~="\0" then
+    t[#t+1]=c
+   else
+    return concat(t)
+   end
+  end
+ end
+end
+
+end -- closure
+
+do -- begin closure to overcome local limits and interference
+
 if not modules then modules={} end modules ['util-str']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
@@ -14,12 +519,13 @@
 utilities=utilities or {}
 utilities.strings=utilities.strings or {}
 local strings=utilities.strings
-local format,gsub,rep,sub,find=string.format,string.gsub,string.rep,string.sub,string.find
+local format,gsub,rep,sub,find,char=string.format,string.gsub,string.rep,string.sub,string.find,string.char
 local load,dump=load,string.dump
 local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable
 local unpack,concat=table.unpack,table.concat
 local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc
 local patterns,lpegmatch=lpeg.patterns,lpeg.match
+local tsplitat=lpeg.tsplitat
 local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len
 local loadstripped=function(str,shortcuts)
  if shortcuts then
@@ -373,6 +879,14 @@
 function string.escapedquotes(s)
  return lpegmatch(pattern,s)
 end
+local pattern=(1-P("\\"))^1;pattern=Cs (
+ pattern*((P("\\")/""*(digit^-3/function(s) return char(tonumber(s)) end))+pattern )^1
+)
+patterns.unescapedquotes=pattern
+function string.unescapedquotes(s)
+ return lpegmatch(pattern,s) or s
+end
+string.texnewlines=lpeg.replacer(patterns.newline,"\r",true)
 local preamble=""
 local environment={
  global=global or _G,
@@ -908,7 +1422,6 @@
  return f_16_16(n/65536.0)
 end
 if not string.explode then
- local tsplitat=lpeg.tsplitat
  local p_utf=patterns.utf8character
  local p_check=C(p_utf)*(P("+")*Cc(true))^0
  local p_split=Ct(C(p_utf)^0)
@@ -928,6 +1441,20 @@
   end
  end
 end
+do
+ local p_whitespace=patterns.whitespace^1
+ local cache=setmetatable({},{ __index=function(t,k)
+  local p=tsplitat(p_whitespace*P(k)*p_whitespace)
+  local v=function(s)
+   return lpegmatch(p,s)
+  end
+  t[k]=v
+  return v
+ end })
+ function string.wordsplitter(s)
+  return cache[s]
+ end
+end
 
 end -- closure
 
@@ -1552,9 +2079,6 @@
   return concat(fastserialize(t,true))
  end
 else
- local f_v=formatters["[%q]=%q,"]
- local f_t=formatters["[%q]="]
- local f_q=formatters["%q,"]
  function table.fastserialize(t,prefix) 
   local r={ type(prefix)=="string" and prefix or "return" }
   local m=1
@@ -1963,6 +2487,21 @@
   return function() end
  end
 end
+function combine(target,source)
+ if target then
+  for k,v in next,source do
+   if type(v)=="table" then
+      target[k]=combine(target[k],source[k])
+   else
+      target[k]=v
+   end
+  end
+  return target
+ else
+  return source
+ end
+end
+table.combine=combine
 
 end -- closure
 
@@ -2181,8 +2720,8 @@
 local nobracket=1-(lbracket+rbracket)
 local escape,left,right=P("\\"),P('{'),P('}')
 lpegpatterns.balanced=P {
- [1]=((escape*(left+right))+(1-(left+right))+V(2))^0,
- [2]=left*V(1)*right
+ ((escape*(left+right))+(1-(left+right))+V(2))^0,
+ left*V(1)*right
 }
 local nestedbraces=P { lbrace*(nobrace+V(1))^0*rbrace }
 local nestedparents=P { lparent*(noparent+V(1))^0*rparent }
@@ -2190,11 +2729,12 @@
 local spaces=space^0
 local argument=Cs((lbrace/"")*((nobrace+nestedbraces)^0)*(rbrace/""))
 local content=(1-endofstring)^0
-lpegpatterns.nestedbraces=nestedbraces  
-lpegpatterns.nestedparents=nestedparents 
-lpegpatterns.nested=nestedbraces  
-lpegpatterns.argument=argument   
-lpegpatterns.content=content    
+lpegpatterns.nestedbraces=nestedbraces   
+lpegpatterns.nestedparents=nestedparents  
+lpegpatterns.nestedbrackets=nestedbrackets 
+lpegpatterns.nested=nestedbraces   
+lpegpatterns.argument=argument    
+lpegpatterns.content=content  
 local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+(1-comma))^0)
 local key=C((1-equal-comma)^1)
 local pattern_a=(space+comma)^0*(key*equal*value+key*C(""))
@@ -2600,15 +3140,24 @@
   action(first)
  end
 end
-local cardinal=lpegpatterns.cardinal/tonumber
+local cardinal=(lpegpatterns.hexadecimal+lpegpatterns.cardinal)/tonumber
 local spacers=lpegpatterns.spacer^0
 local endofstring=lpegpatterns.endofstring
 local stepper=spacers*(cardinal*(spacers*S(":-")*spacers*(cardinal+Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1
 local stepper=spacers*(cardinal*(spacers*S(":-")*spacers*(cardinal+(P("*")+endofstring)*Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1*endofstring 
 function parsers.stepper(str,n,action)
+ local ts=type(str)
  if type(n)=="function" then
-  lpegmatch(stepper,str,1,false,n or print)
- else
+  if ts=="number" then
+   n(str)
+  elseif ts=="table" then
+   for i=1,#str do
+    n(str[i])
+   end
+  else
+   lpegmatch(stepper,str,1,false,n or print)
+  end
+ elseif ts=="string" then
   lpegmatch(stepper,str,1,n,action or print)
  end
 end
@@ -2626,7 +3175,7 @@
 local spaces=lpegpatterns.space^0
 local dummy=function() end
 setmetatableindex(cache,function(t,k)
- local separator=P(k)
+ local separator=S(k) 
  local value=(1-separator)^0
  local pattern=spaces*C(value)*separator^0*Cp()
  t[k]=pattern
@@ -2706,8 +3255,11 @@
 local pattern=Cf(Ct("")*(
   (Cg(Cc("year")*p_year)*S("-/")*Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("day")*cardinal)
   )+(Cg(Cc("day")*cardinal)*S("-/")*Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("year")*p_year)
+  )+(Cg(Cc("year")*p_year)*S("-/")*Cg(Cc("month")*cardinal)
+  )+(Cg(Cc("month")*cardinal)*S("-/")*Cg(Cc("year")*p_year)
   )
- )*P(" ")*Cg(Cc("hour")*cardinal)*P(":")*Cg(Cc("min")*cardinal)*(P(":")*Cg(Cc("sec")*cardinal))^-1
+ )*(
+   P(" ")*Cg(Cc("hour")*cardinal)*P(":")*Cg(Cc("min")*cardinal)*(P(":")*Cg(Cc("sec")*cardinal))^-1+P(-1) )
 ,rawset)
 lpegpatterns.splittime=pattern
 function parsers.totime(str)
@@ -2914,6 +3466,9 @@
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
 }
+if utilities and utilities.json then
+ return json
+end
 local P,V,R,S,C,Cc,Cs,Ct,Cf,Cg=lpeg.P,lpeg.V,lpeg.R,lpeg.S,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cf,lpeg.Cg
 local lpegmatch=lpeg.match
 local format,gsub=string.format,string.gsub
@@ -3118,6 +3673,8 @@
        k=lpegmatch(escaper,k) or k
        v=lpegmatch(escaper,v) or v
        n=n+1 t[n]=f_key_val_str(depth,k,v)
+      elseif i>1 then
+       n=n-1
       end
      elseif tv=="table" then
       local l=#v
@@ -3133,6 +3690,8 @@
        end
       elseif next(v) then
        tojsonpp(v,k,depth,level+1,0)
+      elseif i>1 then
+       n=n-1
       end
      elseif tv=="boolean" then
       if tk=="number" then
@@ -3150,6 +3709,8 @@
        else
         t[n]=f_key_val_nop(depth,k)
        end
+      elseif i>1 then
+       n=n-1
       end
      else
       if tk=="number" then
@@ -3159,6 +3720,8 @@
        k=lpegmatch(escaper,k) or k
        n=n+1
        t[n]=f_key_val_null(depth,k)
+      elseif i>1 then
+       n=n-1
       end
      end
     end
@@ -3260,7 +3823,6 @@
   return jsontostring(value,true)
  end
 end
-return json
 
 end -- closure
 
@@ -3990,6 +4552,22 @@
  end
 end
 debugger.showtraceback=showtraceback
+if luac then
+ local show,dump=luac.print,string.dump
+ function luac.inspect(v)
+  if type(v)=="function" then
+   local ok,str=xpcall(dump,function() end,v)
+   if ok then
+    v=str
+   end
+  end
+  if type(v)=="string" then
+   show(v,true)
+  else
+   print(v)
+  end
+ end
+end
 
 end -- closure
 
@@ -4379,3 +4957,560 @@
 end
 
 end -- closure
+
+do -- begin closure to overcome local limits and interference
+
+if not modules then modules={} end modules ['util-zip']={
+ version=1.001,
+ author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright="PRAGMA ADE / ConTeXt Development Team",
+ license="see context related readme files"
+}
+local type,tostring,tonumber=type,tostring,tonumber
+local sort,concat=table.sort,table.concat
+local find,format,sub,gsub=string.find,string.format,string.sub,string.gsub
+local osdate,ostime,osclock=os.date,os.time,os.clock
+local ioopen=io.open
+local loaddata,savedata=io.loaddata,io.savedata
+local filejoin,isdir,dirname,mkdirs=file.join,lfs.isdir,file.dirname,dir.mkdirs
+local suffix,suffixes=file.suffix,file.suffixes
+local openfile=io.open
+gzip=gzip or {} 
+if not zlib then
+ zlib=xzip 
+elseif not xzip then
+ xzip=zlib
+end
+local files=utilities.files
+local openfile=files.open
+local closefile=files.close
+local readstring=files.readstring
+local readcardinal2=files.readcardinal2le
+local readcardinal4=files.readcardinal4le
+local setposition=files.setposition
+local getposition=files.getposition
+local band=bit32.band
+local rshift=bit32.rshift
+local lshift=bit32.lshift
+local zlibdecompress=zlib.decompress
+local zlibdecompresssize=zlib.decompresssize
+local zlibchecksum=zlib.crc32
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
+ local cs=zlibchecksum
+ zlibchecksum=function(str,n) return cs(n or 0,str) end
+end
+local decompress=function(source)   return zlibdecompress (source,-15)   end 
+local decompresssize=function(source,targetsize) return zlibdecompresssize(source,targetsize,-15) end 
+local calculatecrc=function(buffer,initial) return zlibchecksum   (initial or 0,buffer)   end
+local zipfiles={}
+utilities.zipfiles=zipfiles
+local openzipfile,closezipfile,unzipfile,foundzipfile,getziphash,getziplist  do
+ function openzipfile(name)
+  return {
+   name=name,
+   handle=openfile(name,0),
+  }
+ end
+ local function collect(z)
+  if not z.list then
+   local list={}
+   local hash={}
+   local position=0
+   local index=0
+   local handle=z.handle
+   while true do
+    setposition(handle,position)
+    local signature=readstring(handle,4)
+    if signature=="PK\3\4" then
+     local version=readcardinal2(handle)
+     local flag=readcardinal2(handle)
+     local method=readcardinal2(handle)
+     local filetime=readcardinal2(handle)
+     local filedate=readcardinal2(handle)
+     local crc32=readcardinal4(handle)
+     local compressed=readcardinal4(handle)
+     local uncompressed=readcardinal4(handle)
+     local namelength=readcardinal2(handle)
+     local extralength=readcardinal2(handle)
+     local filename=readstring(handle,namelength)
+     local descriptor=band(flag,8)~=0
+     local encrypted=band(flag,1)~=0
+     local acceptable=method==0 or method==8
+     local skipped=0
+     local size=0
+     if encrypted then
+      size=readcardinal2(handle)
+      skipbytes(size)
+      skipped=skipped+size+2
+      skipbytes(8)
+      skipped=skipped+8
+      size=readcardinal2(handle)
+      skipbytes(size)
+      skipped=skipped+size+2
+      size=readcardinal4(handle)
+      skipbytes(size)
+      skipped=skipped+size+4
+      size=readcardinal2(handle)
+      skipbytes(size)
+      skipped=skipped+size+2
+     end
+     position=position+30+namelength+extralength+skipped
+     if descriptor then
+      setposition(handle,position+compressed)
+      crc32=readcardinal4(handle)
+      compressed=readcardinal4(handle)
+      uncompressed=readcardinal4(handle)
+     end
+     if acceptable then
+      index=index+1
+      local data={
+       filename=filename,
+       index=index,
+       position=position,
+       method=method,
+       compressed=compressed,
+       uncompressed=uncompressed,
+       crc32=crc32,
+       encrypted=encrypted,
+      }
+      hash[filename]=data
+      list[index]=data
+     else
+     end
+     position=position+compressed
+    else
+     break
+    end
+    z.list=list
+    z.hash=hash
+   end
+  end
+ end
+ function getziplist(z)
+  local list=z.list
+  if not list then
+   collect(z)
+  end
+  return z.list
+ end
+ function getziphash(z)
+  local hash=z.hash
+  if not hash then
+   collect(z)
+  end
+  return z.hash
+ end
+ function foundzipfile(z,name)
+  return getziphash(z)[name]
+ end
+ function closezipfile(z)
+  local f=z.handle
+  if f then
+   closefile(f)
+   z.handle=nil
+  end
+ end
+ function unzipfile(z,filename,check)
+  local hash=z.hash
+  if not hash then
+   hash=zipfiles.hash(z)
+  end
+  local data=hash[filename] 
+  if not data then
+  end
+  if data then
+   local handle=z.handle
+   local position=data.position
+   local compressed=data.compressed
+   if compressed>0 then
+    setposition(handle,position)
+    local result=readstring(handle,compressed)
+    if data.method==8 then
+     if decompresssize then
+      result=decompresssize(result,data.uncompressed)
+     else
+      result=decompress(result)
+     end
+    end
+    if check and data.crc32~=calculatecrc(result) then
+     print("checksum mismatch")
+     return ""
+    end
+    return result
+   else
+    return ""
+   end
+  end
+ end
+ zipfiles.open=openzipfile
+ zipfiles.close=closezipfile
+ zipfiles.unzip=unzipfile
+ zipfiles.hash=getziphash
+ zipfiles.list=getziplist
+ zipfiles.found=foundzipfile
+end
+if xzip then 
+ local writecardinal1=files.writebyte
+ local writecardinal2=files.writecardinal2le
+ local writecardinal4=files.writecardinal4le
+ local logwriter=logs.writer
+ local globpattern=dir.globpattern
+ local compress=xzip.compress
+ local checksum=xzip.crc32
+ local function fromdostime(dostime,dosdate)
+  return ostime {
+   year=rshift(dosdate,9)+1980,
+   month=band(rshift(dosdate,5),0x0F),
+   day=band((dosdate   ),0x1F),
+   hour=band(rshift(dostime,11)    ),
+   min=band(rshift(dostime,5),0x3F),
+   sec=band((dostime   ),0x1F),
+  }
+ end
+ local function todostime(time)
+  local t=osdate("*t",time)
+  return
+   lshift(t.year-1980,9)+lshift(t.month,5)+t.day,
+   lshift(t.hour,11)+lshift(t.min,5)+rshift(t.sec,1)
+ end
+ local function openzip(filename,level,comment,verbose)
+  local f=ioopen(filename,"wb")
+  if f then
+   return {
+    filename=filename,
+    handle=f,
+    list={},
+    level=tonumber(level) or 3,
+    comment=tostring(comment),
+    verbose=verbose,
+    uncompressed=0,
+    compressed=0,
+   }
+  end
+ end
+ local function writezip(z,name,data,level,time)
+  local f=z.handle
+  local list=z.list
+  local level=tonumber(level) or z.level or 3
+  local method=8
+  local zipped=compress(data,level)
+  local checksum=checksum(data)
+  local verbose=z.verbose
+  if not zipped then
+   method=0
+   zipped=data
+  end
+  local start=f:seek()
+  local compressed=#zipped
+  local uncompressed=#data
+  z.compressed=z.compressed+compressed
+  z.uncompressed=z.uncompressed+uncompressed
+  if verbose then
+   local pct=100*compressed/uncompressed
+   if pct>=100 then
+    logwriter(format("%10i        %s",uncompressed,name))
+   else
+    logwriter(format("%10i  %02.1f  %s",uncompressed,pct,name))
+   end
+  end
+  f:write("\x50\x4b\x03\x04")
+  writecardinal2(f,0)   
+  writecardinal2(f,0)   
+  writecardinal2(f,method)    
+  writecardinal2(f,0)   
+  writecardinal2(f,0)   
+  writecardinal4(f,checksum)  
+  writecardinal4(f,compressed)   
+  writecardinal4(f,uncompressed) 
+  writecardinal2(f,#name)  
+  writecardinal2(f,0)
+  f:write(name)      
+  f:write(zipped)
+  list[#list+1]={ #zipped,#data,name,checksum,start,time or 0 }
+ end
+ local function closezip(z)
+  local f=z.handle
+  local list=z.list
+  local comment=z.comment
+  local verbose=z.verbose
+  local count=#list
+  local start=f:seek()
+  for i=1,count do
+   local l=list[i]
+   local compressed=l[1]
+   local uncompressed=l[2]
+   local name=l[3]
+   local checksum=l[4]
+   local start=l[5]
+   local time=l[6]
+   local date,time=todostime(time)
+   f:write('\x50\x4b\x01\x02')
+   writecardinal2(f,0)   
+   writecardinal2(f,0)   
+   writecardinal2(f,0)   
+   writecardinal2(f,8)   
+   writecardinal2(f,time)   
+   writecardinal2(f,date)   
+   writecardinal4(f,checksum)  
+   writecardinal4(f,compressed)   
+   writecardinal4(f,uncompressed) 
+   writecardinal2(f,#name)  
+   writecardinal2(f,0)   
+   writecardinal2(f,0)   
+   writecardinal2(f,0)   
+   writecardinal2(f,0)   
+   writecardinal4(f,0)   
+   writecardinal4(f,start)  
+   f:write(name)      
+  end
+  local stop=f:seek()
+  local size=stop-start
+  f:write('\x50\x4b\x05\x06')
+  writecardinal2(f,0)   
+  writecardinal2(f,0)   
+  writecardinal2(f,count)  
+  writecardinal2(f,count)  
+  writecardinal4(f,size)   
+  writecardinal4(f,start)  
+  if type(comment)=="string" and comment~="" then
+   writecardinal2(f,#comment) 
+   f:write(comment)     
+  else
+   writecardinal2(f,0)
+  end
+  if verbose then
+   local compressed=z.compressed
+   local uncompressed=z.uncompressed
+   local filename=z.filename
+   local pct=100*compressed/uncompressed
+   logwriter("")
+   if pct>=100 then
+    logwriter(format("%10i        %s",uncompressed,filename))
+   else
+    logwriter(format("%10i  %02.1f  %s",uncompressed,pct,filename))
+   end
+  end
+  f:close()
+ end
+ local function zipdir(zipname,path,level,verbose)
+  if type(zipname)=="table" then
+   verbose=zipname.verbose
+   level=zipname.level
+   path=zipname.path
+   zipname=zipname.zipname
+  end
+  if not zipname or zipname=="" then
+   return
+  end
+  if not path or path=="" then
+   path="."
+  end
+  if not isdir(path) then
+   return
+  end
+  path=gsub(path,"\\+","/")
+  path=gsub(path,"/+","/")
+  local list={}
+  local count=0
+  globpattern(path,"",true,function(name,size,time)
+   count=count+1
+   list[count]={ name,time }
+  end)
+  sort(list,function(a,b)
+   return a[1]<b[1]
+  end)
+  local zipf=openzip(zipname,level,comment,verbose)
+  if zipf then
+   local p=#path+2
+   for i=1,count do
+    local li=list[i]
+    local name=li[1]
+    local time=li[2]
+    local data=loaddata(name)
+    local name=sub(name,p,#name)
+    writezip(zipf,name,data,level,time,verbose)
+   end
+   closezip(zipf)
+  end
+ end
+ local function unzipdir(zipname,path,verbose)
+  if type(zipname)=="table" then
+   verbose=zipname.verbose
+   path=zipname.path
+   zipname=zipname.zipname
+  end
+  if not zipname or zipname=="" then
+   return
+  end
+  if not path or path=="" then
+   path="."
+  end
+  local z=openzipfile(zipname)
+  if z then
+   local list=getziplist(z)
+   if list then
+    local total=0
+    local count=#list
+    local step=number.idiv(count,10)
+    local done=0
+    local steps=verbose=="steps"
+    local time=steps and osclock()
+    for i=1,count do
+     local l=list[i]
+     local n=l.filename
+     local d=unzipfile(z,n) 
+     if d then
+      local p=filejoin(path,n)
+      if mkdirs(dirname(p)) then
+       if steps then
+        total=total+#d
+        done=done+1
+        if done>=step then
+         done=0
+         logwriter(format("%4i files of %4i done, %10i bytes, %0.3f seconds",i,count,total,osclock()-time))
+        end
+       elseif verbose then
+        logwriter(n)
+       end
+       savedata(p,d)
+      end
+     else
+      logwriter(format("problem with file %s",n))
+     end
+    end
+    if steps then
+     logwriter(format("%4i files of %4i done, %10i bytes, %0.3f seconds",count,count,total,osclock()-time))
+    end
+    closezipfile(z)
+    return true
+   else
+    closezipfile(z)
+   end
+  end
+ end
+ zipfiles.zipdir=zipdir
+ zipfiles.unzipdir=unzipdir
+end
+local pattern="^\x1F\x8B\x08"
+local gziplevel=3
+function gzip.suffix(filename)
+ local suffix,extra=suffixes(filename)
+ local gzipped=extra=="gz"
+ return suffix,gzipped
+end
+function gzip.compressed(s)
+ return s and find(s,pattern)
+end
+local getdecompressed
+local putcompressed
+if gzip.compress then
+ local gzipwindow=15+16 
+ local compress=zlib.compress
+ local decompress=zlib.decompress
+ getdecompressed=function(str)
+  return decompress(str,gzipwindow) 
+ end
+ putcompressed=function(str,level)
+  return compress(str,level or gziplevel,nil,gzipwindow)
+ end
+else
+ local gzipwindow=-15 
+ local identifier="\x1F\x8B"
+ local compress=zlib.compress
+ local decompress=zlib.decompress
+ local zlibchecksum=zlib.crc32
+ if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
+  local cs=zlibchecksum
+  zlibchecksum=function(str,n) return cs(n or 0,str) end
+ end
+ local streams=utilities.streams
+ local openstream=streams.openstring
+ local closestream=streams.close
+ local getposition=streams.getposition
+ local readbyte=streams.readbyte
+ local readcardinal4=streams.readcardinal4le
+ local readcardinal2=streams.readcardinal2le
+ local readstring=streams.readstring
+ local readcstring=streams.readcstring
+ local skipbytes=streams.skip
+ local tocardinal1=streams.tocardinal1
+ local tocardinal4=streams.tocardinal4le
+ getdecompressed=function(str)
+  local s=openstream(str)
+  local identifier=readstring(s,2)
+  local method=readbyte(s,1)
+  local flags=readbyte(s,1)
+  local timestamp=readcardinal4(s)
+  local compression=readbyte(s,1)
+  local operating=readbyte(s,1)
+  local isjusttext=band(flags,0x01)~=0 and true    or false
+  local extrasize=band(flags,0x04)~=0 and readcardinal2(s) or 0
+  local filename=band(flags,0x08)~=0 and readcstring(s)   or ""
+  local comment=band(flags,0x10)~=0 and readcstring(s)   or ""
+  local checksum=band(flags,0x02)~=0 and readcardinal2(s) or 0
+  local compressed=readstring(s,#str)
+  local data=decompress(compressed,gzipwindow) 
+  return data
+ end
+ putcompressed=function(str,level,originalname)
+  return concat {
+   identifier,
+   tocardinal1(0x08),
+   tocardinal1(0x08),
+   tocardinal4(os.time()),
+   tocardinal1(0x02),
+   tocardinal1(0xFF),
+   (originalname or "unknownname").."\0",
+   compress(str,level,nil,gzipwindow),
+   tocardinal4(zlibchecksum(str)),
+   tocardinal4(#str),
+  }
+ end
+end
+function gzip.load(filename)
+ local f=openfile(filename,"rb")
+ if not f then
+ else
+  local data=f:read("*all")
+  f:close()
+  if data and data~="" then
+   if suffix(filename)=="gz" then
+    data=getdecompressed(data)
+   end
+   return data
+  end
+ end
+end
+function gzip.save(filename,data,level,originalname)
+ if suffix(filename)~="gz" then
+  filename=filename..".gz"
+ end
+ local f=openfile(filename,"wb")
+ if f then
+  data=putcompressed(data or "",level or gziplevel,originalname)
+  f:write(data)
+  f:close()
+  return #data
+ end
+end
+function gzip.compress(s,level)
+ if s and not find(s,pattern) then
+  if not level then
+   level=gziplevel
+  elseif level<=0 then
+   return s
+  elseif level>9 then
+   level=9
+  end
+  return putcompressed(s,level or gziplevel) or s
+ end
+end
+function gzip.decompress(s)
+ if s and find(s,pattern) then
+  return getdecompressed(s)
+ else
+  return s
+ end
+end
+
+end -- closure

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -30,8 +30,8 @@
 
 local lualibs_extended_module = {
   name          = "lualibs-extended",
-  version       = "2.74",       --TAGVERSION
-  date          = "2021-05-20", --TAGDATE
+  version       = "2.75",       --TAGVERSION
+  date          = "2022-10-04", --TAGDATE
   description   = "ConTeXt Lua libraries -- extended collection.",
   author        = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang",
   copyright     = "PRAGMA ADE / ConTeXt Development Team",
@@ -119,6 +119,7 @@
 end
 
 if loaded == false then
+  loadmodule("lualibs-util-sac.lua")--- streams: string based file parsers
   loadmodule("lualibs-util-str.lua")--- string formatters (fast)
   loadmodule("lualibs-util-fil.lua")--- extra file helpers
   loadmodule("lualibs-util-tab.lua")--- extended table operations
@@ -138,6 +139,7 @@
   loadmodule("lualibs-util-deb.lua")--- extra debugging
   loadmodule("lualibs-util-tpl.lua")--- templating
   loadmodule("lualibs-util-sta.lua")--- stacker (for writing pdf)
+  loadmodule("lualibs-util-zip.lua")--- compression and zip files
 end
 
 unfake_context() --- TODO check if this works at runtime

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -456,7 +456,7 @@
     if not two then
         return one == "" and one or lpegmatch(reslasher,one)
     end
-    if one == "" then
+    if not one or one == "" then
         return lpegmatch(stripper,three and concat({ two, three, ... },"/") or two)
     end
     if lpegmatch(isnetwork,one) then

Deleted: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-gzip.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-gzip.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-gzip.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -1,180 +0,0 @@
-if not modules then modules = { } end modules ['l-gzip'] = {
-    version   = 1.001,
-    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
-    copyright = "PRAGMA ADE / ConTeXt Development Team",
-    license   = "see context related readme files"
-}
-
--- We only have a few official methods here:
---
---   local decompressed = gzip.load       (filename)
---   local resultsize   = gzip.save       (filename,compresslevel)
---   local compressed   = gzip.compress   (str,compresslevel)
---   local decompressed = gzip.decompress (str)
---   local iscompressed = gzip.compressed (str)
---   local suffix, okay = gzip.suffix     (filename)
---
--- In LuaMetaTeX we have only xzip which implements a very few methods:
---
---   compress   (str,level,method,window,memory,strategy)
---   decompress (str,window)
---   adler32    (str,checksum)
---   crc32      (str,checksum)
---
--- Special window values are:
---
--- flate : - 15
--- zlib  :   15
--- gzip  :   15 | 16
--- auto  :   15 | 32
-
-gzip = gzip or { } -- so in luatex we keep the old ones too
-
-if not zlib then
-    zlib = xzip    -- in luametatex we shadow the old one
-elseif not xzip then
-    xzip = zlib
-end
-
-if zlib then
-
-    local suffix     = file.suffix
-    local suffixes   = file.suffixes
-    local find       = string.find
-    local openfile   = io.open
-
-    local gzipwindow = 15 + 16 -- +16: gzip, +32: gzip|zlib
-    local gziplevel  = 3
-    local identifier = "^\x1F\x8B\x08"
-
-    local compress   = zlib.compress
-    local decompress = zlib.decompress
-
-    function gzip.load(filename)
-        local f = openfile(filename,"rb")
-        if not f then
-            -- invalid file
-        else
-            local data = f:read("*all")
-            f:close()
-            if data and data ~= "" then
-                if suffix(filename) == "gz" then
-                    data = decompress(data,gzipwindow)
-                end
-                return data
-            end
-        end
-    end
-
-    function gzip.save(filename,data,level)
-        if suffix(filename) ~= "gz" then
-            filename = filename .. ".gz"
-        end
-        local f = openfile(filename,"wb")
-        if f then
-            data = compress(data or "",level or gziplevel,nil,gzipwindow)
-            f:write(data)
-            f:close()
-            return #data
-        end
-    end
-
-    function gzip.suffix(filename)
-        local suffix, extra = suffixes(filename)
-        local gzipped = extra == "gz"
-        return suffix, gzipped
-    end
-
-    function gzip.compressed(s)
-        return s and find(s,identifier)
-    end
-
-    function gzip.compress(s,level)
-        if s and not find(s,identifier) then -- the find check might go away
-            if not level then
-                level = gziplevel
-            elseif level <= 0 then
-                return s
-            elseif level > 9 then
-                level = 9
-            end
-            return compress(s,level or gziplevel,nil,gzipwindow) or s
-        end
-    end
-
-    function gzip.decompress(s)
-        if s and find(s,identifier) then
-            return decompress(s,gzipwindow)
-        else
-            return s
-        end
-    end
-
-end
-
--- In luametatex we can use this one but it doesn't look like there wil be stream
--- support so for now we still use zlib (the performance difference is not that
--- spectacular in our usage.
-
--- if flate then
---
---     local type = type
---     local find = string.find
---
---     local compress   = flate.gz_compress
---     local decompress = flate.gz_decompress
---
---     local absmax     = 128*1024*1024
---     local initial    =       64*1024
---     local identifier = "^\x1F\x8B\x08"
---
---     function gzip.compressed(s)
---         return s and find(s,identifier)
---     end
---
---     function gzip.compress(s,level)
---         if s and not find(s,identifier) then -- the find check might go away
---             if not level then
---                 level = 3
---             elseif level <= 0 then
---                 return s
---             elseif level > 9 then
---                 level = 9
---             end
---             return compress(s,level) or s
---         end
---     end
---
---     function gzip.decompress(s,size,iterate)
---         if s and find(s,identifier) then
---             if type(size) ~= "number" then
---                 size = initial
---             end
---             if size > absmax then
---                 size = absmax
---             end
---             if type(iterate) == "number" then
---                 max = size * iterate
---             elseif iterate == nil or iterate == true then
---                 iterate = true
---                 max     = absmax
---             end
---             if max > absmax then
---                 max = absmax
---             end
---             while true do
---                 local d = decompress(s,size)
---                 if d then
---                     return d
---                 end
---                 size = 2 * size
---                 if not iterate or size > max then
---                     return false
---                 end
---             end
---         else
---             return s
---         end
---     end
---
--- end

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -147,9 +147,12 @@
     end
 end
 
-function io.savedata(filename,data,joiner)
-    local f = open(filename,"wb")
+function io.savedata(filename,data,joiner,append)
+    local f = open(filename,append and "ab" or "wb")
     if f then
+        if append and joiner and f:seek("end") > 0 then
+            f:write(joiner)
+        end
         if type(data) == "table" then
             f:write(concat(data,joiner or ""))
         elseif type(data) == "function" then
@@ -288,7 +291,8 @@
 
 io.noflines = noflines
 
--- inlined is faster ... beware, better use util-fil
+-- inlined is faster ... beware, better use util-fil so these are obsolete
+-- and will go
 
 local nextchar = {
     [ 4] = function(f)

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -665,12 +665,12 @@
 -- lpeg.print(lpeg.P("a","b","c"))
 -- lpeg.print(lpeg.S("a","b","c"))
 
--- print(lpeg.count("äáàa",lpeg.P("á") + lpeg.P("à")))
--- print(lpeg.count("äáàa",lpeg.UP("áà")))
--- print(lpeg.count("äáàa",lpeg.US("àá")))
--- print(lpeg.count("äáàa",lpeg.UR("aá")))
--- print(lpeg.count("äáàa",lpeg.UR("àá")))
--- print(lpeg.count("äáàa",lpeg.UR(0x0000,0xFFFF)))
+-- print(lpeg.counter(lpeg.P("á") + lpeg.P("à"))("äáàa"))
+-- print(lpeg.counter(lpeg.UP("áà"))("äáàa"))
+-- print(lpeg.counter(lpeg.US("àá"))("äáàa"))
+-- print(lpeg.counter(lpeg.UR("aá"))("äáàa"))
+-- print(lpeg.counter(lpeg.UR("àá"))("äáàa"))
+-- print(lpeg.counter(lpeg.UR(0x0000,0xFFFF)))
 
 function lpeg.is_lpeg(p)
     return p and lpegtype(p) == "pattern"

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -14,10 +14,20 @@
 
 if not math.round then
 
-    local floor = math.floor
+    if xmath then
 
-    function math.round(x) return floor(x + 0.5) end
+        math.round = xmath.round
 
+    else
+
+        local floor = math.floor
+
+        function math.round(x)
+            return x < 0 and -floor(-x + 0.5) or floor(x + 0.5)
+        end
+
+    end
+
 end
 
 if not math.div then
@@ -141,7 +151,7 @@
 
     local floor = math.floor
 
-    function math.tointeger(m,n)
+    function math.ult(m,n)
         -- not ok but i'm not motivated to look into it now
         return floor(m) < floor(n) -- unsigned comparison needed
     end

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -26,10 +26,10 @@
 -- math.randomseed(tonumber(string.sub(string.reverse(tostring(math.floor(socket.gettime()*10000))),1,6)))
 
 local os = os
-local date, time = os.date, os.time
+local date, time, difftime = os.date, os.time, os.difftime
 local find, format, gsub, upper, gmatch = string.find, string.format, string.gsub, string.upper, string.gmatch
 local concat = table.concat
-local random, ceil, randomseed = math.random, math.ceil, math.randomseed
+local random, ceil, randomseed, modf = math.random, math.ceil, math.randomseed, math.modf
 local type, setmetatable, tonumber, tostring = type, setmetatable, tonumber, tostring
 
 -- This check needs to happen real early on. Todo: we can pick it up from the commandline
@@ -36,12 +36,17 @@
 -- if we pass --binpath= (which is useful anyway)
 
 do
+
     local selfdir = os.selfdir
+
     if selfdir == "" then
         selfdir = nil
     end
+
     if not selfdir then
+
         -- We need a fallback plan so let's see what we get.
+
         if arg then
             -- passed by mtx-context ... saves network access
             for i=1,#arg do
@@ -52,6 +57,7 @@
                 end
             end
         end
+
         if not selfdir then
             selfdir = os.selfbin or "luatex"
             if find(selfdir,"[/\\]") then
@@ -92,11 +98,16 @@
                 end
             end
         end
+
         -- let's hope we're okay now
+
         os.selfdir = selfdir or "."
+
     end
+
+    -- print(os.selfdir) os.exit()
+
 end
--- print(os.selfdir) os.exit()
 
 -- The following code permits traversing the environment table, at least in luatex. Internally all
 -- environment names are uppercase.
@@ -157,7 +168,7 @@
         end
 
         function os.getenv(k)
-            local K = upper(k)
+            local K = upper(k) -- hm utf
             local v = osenv[K] or osgetenv(K) or osgetenv(k)
             if v == "" then
                 return nil
@@ -183,32 +194,14 @@
 
 -- end of environment hack
 
-local execute = os.execute
-local iopopen = io.popen
+if not io.fileseparator then
 
-local function resultof(command)
-    local handle = iopopen(command,"r") -- already has flush
-    if handle then
-        local result = handle:read("*all") or ""
-        handle:close()
-        return result
-    else
-        return ""
-    end
-end
-
-os.resultof = resultof
-
-function os.pipeto(command)
-    return iopopen(command,"w") -- already has flush
-end
-
-if not io.fileseparator then
     if find(os.getenv("PATH"),";",1,true) then
         io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "windows"
     else
         io.fileseparator, io.pathseparator, os.type = "/" , ":", os.type or "unix"
     end
+
 end
 
 os.type = os.type or (io.pathseparator == ";"       and "windows") or "unix"
@@ -220,351 +213,370 @@
     os.libsuffix, os.binsuffix, os.binsuffixes = 'so', '', { '' }
 end
 
-local launchers = {
-    windows = "start %s",
-    macosx  = "open %s",
-    unix    = "xdg-open %s &> /dev/null &",
-}
+do
 
-function os.launch(str)
-    local command = format(launchers[os.name] or launchers.unix,str)
-    -- todo: pcall
---     print(command)
-    execute(command)
-end
+    local execute = os.execute
+    local iopopen = io.popen
+    local ostype  = os.type
 
-local gettimeofday = os.gettimeofday or os.clock
-os.gettimeofday    = gettimeofday
+    local function resultof(command)
+        -- already has flush, b is new and we need it to pipe xz output
+        local handle = iopopen(command,ostype == "windows" and "rb" or "r")
+        if handle then
+            local result = handle:read("*all") or ""
+            handle:close()
+            return result
+        else
+            return ""
+        end
+    end
 
-local startuptime = gettimeofday()
+    os.resultof = resultof
 
-function os.runtime()
-    return gettimeofday() - startuptime
+    function os.pipeto(command)
+        return iopopen(command,"w") -- already has flush
+    end
+
+    local launchers = {
+        windows = "start %s",
+        macosx  = "open %s",
+        unix    = "xdg-open %s &> /dev/null &",
+    }
+
+    function os.launch(str)
+        local command = format(launchers[os.name] or launchers.unix,str)
+        -- todo: pcall
+    --     print(command)
+        execute(command)
+    end
+
 end
 
--- print(os.gettimeofday()-os.time())
--- os.sleep(1.234)
--- print (">>",os.runtime())
--- print(os.date("%H:%M:%S",os.gettimeofday()))
--- print(os.date("%H:%M:%S",os.time()))
+do
 
--- no need for function anymore as we have more clever code and helpers now
--- this metatable trickery might as well disappear
+    local gettimeofday = os.gettimeofday or os.clock
+    os.gettimeofday    = gettimeofday
 
-local resolvers = os.resolvers or { }
-os.resolvers    = resolvers
+    local startuptime = gettimeofday()
 
-setmetatable(os, { __index = function(t,k)
-    local r = resolvers[k]
-    return r and r(t,k) or nil -- no memoize
-end })
+    function os.runtime()
+        return gettimeofday() - startuptime
+    end
 
--- we can use HOSTTYPE on some platforms
+    -- print(os.gettimeofday()-os.time())
+    -- os.sleep(1.234)
+    -- print (">>",os.runtime())
+    -- print(os.date("%H:%M:%S",os.gettimeofday()))
+    -- print(os.date("%H:%M:%S",os.time()))
 
-local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or ""
+end
 
--- local function guess()
---     local architecture = resultof("uname -m") or ""
---     if architecture ~= "" then
---         return architecture
---     end
---     architecture = os.getenv("HOSTTYPE") or ""
---     if architecture ~= "" then
---         return architecture
---     end
---     return resultof("echo $HOSTTYPE") or ""
--- end
-
+-- We can use HOSTTYPE on some platforms (but not consistently on e.g. Linux).
+--
 -- os.bits = 32 | 64
+--
+-- os.uname() : return {
+--     machine  = "x86_64",
+--     nodename = "MYLAPTOP",
+--     release  = "build 9200",
+--     sysname  = "Windows",
+--     version  = "6.02",
+-- }
 
--- os.uname()
---     sysname
---     machine
---     release
---     version
---     nodename
+do
 
-if platform ~= "" then
+    local name         = os.name or "linux"
+    local platform     = os.getenv("MTX_PLATFORM") or ""
+    local architecture = os.uname and os.uname().machine -- lmtx
+    local bits         = os.getenv("MTX_BITS") or find(platform,"64") and 64 or 32
 
-    os.platform = platform
+    if platform ~= "" then
 
-elseif os.type == "windows" then
+        -- we're okay already
 
-    -- we could set the variable directly, no function needed here
+    elseif os.type == "windows" then
 
-    -- PROCESSOR_ARCHITECTURE : binary platform
-    -- PROCESSOR_ARCHITEW6432 : OS platform
+        -- PROCESSOR_ARCHITECTURE : binary platform
+        -- PROCESSOR_ARCHITEW6432 : OS platform
 
-    -- mswin-64 is now win64
-
-    function resolvers.platform(t,k)
-        local architecture = os.getenv("PROCESSOR_ARCHITECTURE") or ""
-        local platform     = ""
-        if find(architecture,"AMD64",1,true) then
-            platform = "win64"
+        architecture = string.lower(architecture or os.getenv("PROCESSOR_ARCHITECTURE") or "")
+        if architecture == "x86_64" then
+            bits, platform = 64, "win64"
+        elseif find(architecture,"amd64") then
+            bits, platform = 64, "win64"
+        elseif find(architecture,"arm64") then
+            bits, platform = 64, "windows-arm64"
+        elseif find(architecture,"arm32") then
+            bits, platform = 32, "windows-arm32"
         else
-            platform = "mswin"
+            bits, platform = 32, "mswin"
         end
-        os.setenv("MTX_PLATFORM",platform)
-        os.platform = platform
-        return platform
-    end
 
-elseif name == "linux" then
+    elseif name == "linux" then
 
-    function resolvers.platform(t,k)
-        -- we sometimes have HOSTTYPE set so let's check that first
-        local architecture = os.getenv("HOSTTYPE") or resultof("uname -m") or ""
-        local platform     = os.getenv("MTX_PLATFORM") or ""
-        local musl         = find(os.selfdir or "","linuxmusl")
-        if platform ~= "" then
-            -- we're done
-        elseif find(architecture,"x86_64",1,true) then
-            platform = musl and "linuxmusl" or "linux-64"
-        elseif find(architecture,"ppc",1,true) then
-            platform = "linux-ppc"
+        -- There is no way to detect if musl is used because there is no __MUSL__
+        -- and it looks like there never will be. Folks don't care about cases where
+        -- one ships multipe binaries (as with TeX distibutions) and want to select
+        -- the right one. So probably it expects users to compile locally in which
+        -- case we don't care to much as they can then sort it out.
+
+        architecture = architecture or os.getenv("HOSTTYPE") or resultof("uname -m") or ""
+        local musl = find(os.selfdir or "","linuxmusl")
+        if find(architecture,"x86_64") then
+            bits, platform = 64, musl and "linuxmusl" or "linux-64"
+        elseif find(architecture,"ppc") then
+            bits, platform = 32, "linux-ppc" -- this will be dropped
         else
-            platform = musl and "linuxmusl" or "linux"
+            bits, platform = 32, musl and "linuxmusl" or "linux"
         end
-        os.setenv("MTX_PLATFORM",platform)
-        os.platform = platform
-        return platform
-    end
 
-elseif name == "macosx" then
+    elseif name == "macosx" then
 
-    --[[
-        Identifying the architecture of OSX is quite a mess and this
-        is the best we can come up with. For some reason $HOSTTYPE is
-        a kind of pseudo environment variable, not known to the current
-        environment. And yes, uname cannot be trusted either, so there
-        is a change that you end up with a 32 bit run on a 64 bit system.
-        Also, some proper 64 bit intel macs are too cheap (low-end) and
-        therefore not permitted to run the 64 bit kernel.
-      ]]--
+        -- Identifying the architecture of OSX is quite a mess and this is the best
+        -- we can come up with. For some reason $HOSTTYPE is a kind of pseudo
+        -- environment variable, not known to the current environment. And yes,
+        -- uname cannot be trusted either, so there is a change that you end up with
+        -- a 32 bit run on a 64 bit system. Also, some proper 64 bit intel macs are
+        -- too cheap (low-end) and therefore not permitted to run the 64 bit kernel.
 
-    function resolvers.platform(t,k)
-     -- local platform     = ""
-     -- local architecture = os.getenv("HOSTTYPE") or ""
-     -- if architecture == "" then
-     --     architecture = resultof("echo $HOSTTYPE") or ""
-     -- end
-        local architecture = resultof("echo $HOSTTYPE") or ""
-        local platform     = ""
+        architecture = architecture or resultof("echo $HOSTTYPE") or ""
         if architecture == "" then
-         -- print("\nI have no clue what kind of OSX you're running so let's assume an 32 bit intel.\n")
-            platform = "osx-intel"
-        elseif find(architecture,"i386",1,true) then
-            platform = "osx-intel"
-        elseif find(architecture,"x86_64",1,true) then
-            platform = "osx-64"
-        elseif find(architecture,"arm64",1,true) then
-            platform = "osx-arm"
+            bits, platform = 64, "osx-intel"
+        elseif find(architecture,"i386") then
+            bits, platform = 64, "osx-intel"
+        elseif find(architecture,"x86_64") then
+            bits, platform = 64, "osx-64"
+        elseif find(architecture,"arm64") then
+            bits, platform = 64, "osx-arm"
         else
-            platform = "osx-ppc"
+            bits, platform = 32, "osx-ppc"
         end
-        os.setenv("MTX_PLATFORM",platform)
-        os.platform = platform
-        return platform
-    end
 
-elseif name == "sunos" then
+    elseif name == "sunos" then
 
-    function resolvers.platform(t,k)
-        local architecture = resultof("uname -m") or ""
-        local platform     = ""
-        if find(architecture,"sparc",1,true) then
-            platform = "solaris-sparc"
+        architecture = architecture or resultof("uname -m") or ""
+        if find(architecture,"sparc") then
+            bits, platform = 32, "solaris-sparc"
         else -- if architecture == 'i86pc'
-            platform = "solaris-intel"
+            bits, platform = 32, "solaris-intel"
         end
-        os.setenv("MTX_PLATFORM",platform)
-        os.platform = platform
-        return platform
-    end
 
-elseif name == "freebsd" then
+    elseif name == "freebsd" then
 
-    function resolvers.platform(t,k)
-        local architecture = resultof("uname -m") or ""
-        local platform     = ""
-        if find(architecture,"amd64",1,true) then
-            platform = "freebsd-amd64"
+        architecture = architecture or os.getenv("MACHTYPE") or resultof("uname -m") or ""
+        if find(architecture,"amd64") or find(architecture,"AMD64") then
+            bits, platform = 64, "freebsd-amd64"
         else
-            platform = "freebsd"
+            bits, platform = 32, "freebsd"
         end
-        os.setenv("MTX_PLATFORM",platform)
-        os.platform = platform
-        return platform
-    end
 
-elseif name == "kfreebsd" then
+    elseif name == "kfreebsd" then
 
-    function resolvers.platform(t,k)
-        -- we sometimes have HOSTTYPE set so let's check that first
-        local architecture = os.getenv("HOSTTYPE") or resultof("uname -m") or ""
-        local platform     = ""
-        if find(architecture,"x86_64",1,true) then
-            platform = "kfreebsd-amd64"
+        architecture = architecture or os.getenv("HOSTTYPE") or resultof("uname -m") or ""
+        if architecture == "x86_64" then
+            bits, platform = 64, "kfreebsd-amd64"
         else
-            platform = "kfreebsd-i386"
+            bits, platform = 32, "kfreebsd-i386"
         end
-        os.setenv("MTX_PLATFORM",platform)
-        os.platform = platform
-        return platform
-    end
 
-else
+    else
 
-    -- platform = "linux"
-    -- os.setenv("MTX_PLATFORM",platform)
-    -- os.platform = platform
+        architecture = architecture or resultof("uname -m") or ""
 
-    function resolvers.platform(t,k)
-        local platform = "linux"
-        os.setenv("MTX_PLATFORM",platform)
-        os.platform = platform
-        return platform
+        if find(architecture,"aarch64") then
+            bits, platform = "linux-aarch64"
+        elseif find(architecture,"armv7l") then
+            -- linux-armel
+            bits, platform = 32, "linux-armhf"
+        elseif find(architecture,"mips64") or find(architecture,"mips64el") then
+            bits, platform = 64, "linux-mipsel"
+        elseif find(architecture,"mipsel") or find(architecture,"mips") then
+            bits, platform = 32, "linux-mipsel"
+        else
+            bits, platform = 64, "linux-64" -- was 32, "linux"
+        end
+
     end
 
-end
+    os.setenv("MTX_PLATFORM",platform)
+    os.setenv("MTX_BITS",    bits)
 
-os.newline = name == "windows" and "\013\010" or "\010" -- crlf or lf
+    os.platform = platform
+    os.bits     = bits
+    os.newline  = name == "windows" and "\013\010" or "\010" -- crlf or lf
 
-function resolvers.bits(t,k)
-    local bits = find(os.platform,"64",1,true) and 64 or 32
-    os.bits = bits
-    return bits
 end
 
 -- beware, we set the randomseed
 
--- from wikipedia: Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the
--- version number as well as two reserved bits. All other bits are set using a random or pseudorandom
--- data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal
--- digits x and hexadecimal digits 8, 9, A, or B for y. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479.
---
--- as we don't call this function too often there is not so much risk on repetition
+-- From wikipedia: Version 4 UUIDs use a scheme relying only on random numbers. This
+-- algorithm sets the version number as well as two reserved bits. All other bits
+-- are set using a random or pseudorandom data source. Version 4 UUIDs have the form
+-- xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal digits x and hexadecimal
+-- digits 8, 9, A, or B for y. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479. As we don't
+-- call this function too often there is not so much risk on repetition.
 
-local t = { 8, 9, "a", "b" }
+do
 
-function os.uuid()
-    return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
-        random(0xFFFF),random(0xFFFF),
-        random(0x0FFF),
-        t[ceil(random(4))] or 8,random(0x0FFF),
-        random(0xFFFF),
-        random(0xFFFF),random(0xFFFF),random(0xFFFF)
-    )
+    local t = { 8, 9, "a", "b" }
+
+    function os.uuid()
+        return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
+            random(0xFFFF),random(0xFFFF),
+            random(0x0FFF),
+            t[ceil(random(4))] or 8,random(0x0FFF),
+            random(0xFFFF),
+            random(0xFFFF),random(0xFFFF),random(0xFFFF)
+        )
+    end
+
 end
 
-local d
+do
 
-function os.timezone(delta)
-    d = d or ((tonumber(date("%H")) or 0) - (tonumber(date("!%H")) or 0))
-    if delta then
-        if d > 0 then
-            return format("+%02i:00",d)
+    -- this is fragile because it depends on time and so we only check once during
+    -- a run (the computer doesn't move zones) .. Michal Vlasák made a better one
+
+ -- local d
+ --
+ -- function os.timezone()
+ --     d = d or ((tonumber(date("%H")) or 0) - (tonumber(date("!%H")) or 0))
+ --     if d > 0 then
+ --         return format("+%02i:00",d)
+ --     else
+ --         return format("-%02i:00",-d)
+ --     end
+ -- end
+
+    local hour, min
+
+    function os.timezone(difference)
+        if not hour then
+            -- somehow looks too complex:
+            local current   = time()
+            local utcdate   = date("!*t", current)
+            local localdate = date("*t", current)
+            localdate.isdst = false
+            local timediff  = difftime(time(localdate), time(utcdate))
+            hour, min = modf(timediff / 3600)
+            min = min * 60
+        end
+        if difference then
+            return hour, min
         else
-            return format("-%02i:00",-d)
+            return format("%+03d:%02d",hour,min) -- %+ means: always show sign
         end
-    else
-        return 1
     end
-end
 
-local timeformat = format("%%s%s",os.timezone(true))
-local dateformat = "!%Y-%m-%d %H:%M:%S"
-local lasttime   = nil
-local lastdate   = nil
+    -- localtime with timezone: 2021-10-22 10:22:54+02:00
 
-function os.fulltime(t,default)
-    t = t and tonumber(t) or 0
-    if t > 0 then
-        -- valid time
-    elseif default then
-        return default
-    else
-        t = time()
+    local timeformat = format("%%s%s",os.timezone())
+    local dateformat = "%Y-%m-%d %H:%M:%S"
+    local lasttime   = nil
+    local lastdate   = nil
+
+    function os.fulltime(t,default)
+        t = t and tonumber(t) or 0
+        if t > 0 then
+            -- valid time
+        elseif default then
+            return default
+        else
+            t = time()
+        end
+        if t ~= lasttime then
+            lasttime = t
+            lastdate = format(timeformat,date(dateformat))
+        end
+        return lastdate
     end
-    if t ~= lasttime then
-        lasttime = t
-        lastdate = format(timeformat,date(dateformat))
+
+    -- localtime without timezone: 2021-10-22 10:22:54
+
+    local dateformat = "%Y-%m-%d %H:%M:%S"
+    local lasttime   = nil
+    local lastdate   = nil
+
+    function os.localtime(t,default)
+        t = t and tonumber(t) or 0
+        if t > 0 then
+            -- valid time
+        elseif default then
+            return default
+        else
+            t = time()
+        end
+        if t ~= lasttime then
+            lasttime = t
+            lastdate = date(dateformat,t)
+        end
+        return lastdate
     end
-    return lastdate
-end
 
-local dateformat = "%Y-%m-%d %H:%M:%S"
-local lasttime   = nil
-local lastdate   = nil
+    function os.converttime(t,default)
+        local t = tonumber(t)
+        if t and t > 0 then
+            return date(dateformat,t)
+        else
+            return default or "-"
+        end
+    end
 
-function os.localtime(t,default)
-    t = t and tonumber(t) or 0
-    if t > 0 then
-        -- valid time
-    elseif default then
-        return default
-    else
-        t = time()
+    -- table with values
+
+    function os.today()
+        return date("!*t")
     end
-    if t ~= lasttime then
-        lasttime = t
-        lastdate = date(dateformat,t)
+
+    -- utc time without timezone: 2021-10-22 08:22:54
+
+    function os.now()
+        return date("!%Y-%m-%d %H:%M:%S")
     end
-    return lastdate
-end
 
-function os.converttime(t,default)
-    local t = tonumber(t)
-    if t and t > 0 then
-        return date(dateformat,t)
-    else
-        return default or "-"
-    end
 end
 
-local memory = { }
+do
 
-local function which(filename)
-    local fullname = memory[filename]
-    if fullname == nil then
-        local suffix = file.suffix(filename)
-        local suffixes = suffix == "" and os.binsuffixes or { suffix }
-        for directory in gmatch(os.getenv("PATH"),"[^" .. io.pathseparator .."]+") do
-            local df = file.join(directory,filename)
-            for i=1,#suffixes do
-                local dfs = file.addsuffix(df,suffixes[i])
-                if io.exists(dfs) then
-                    fullname = dfs
-                    break
+    local cache = { }
+
+    local function which(filename)
+        local fullname = cache[filename]
+        if fullname == nil then
+            local suffix = file.suffix(filename)
+            local suffixes = suffix == "" and os.binsuffixes or { suffix }
+            for directory in gmatch(os.getenv("PATH"),"[^" .. io.pathseparator .."]+") do
+                local df = file.join(directory,filename)
+                for i=1,#suffixes do
+                    local dfs = file.addsuffix(df,suffixes[i])
+                    if io.exists(dfs) then
+                        fullname = dfs
+                        break
+                    end
                 end
             end
+            if not fullname then
+                fullname = false
+            end
+            cache[filename] = fullname
         end
-        if not fullname then
-            fullname = false
-        end
-        memory[filename] = fullname
+        return fullname
     end
-    return fullname
-end
 
-os.which = which
-os.where = which
+    os.which = which
+    os.where = which
 
-function os.today()
-    return date("!*t") -- table with values
-end
+    -- print(os.which("inkscape.exe"))
+    -- print(os.which("inkscape"))
+    -- print(os.which("gs.exe"))
+    -- print(os.which("ps2pdf"))
 
-function os.now()
-    return date("!%Y-%m-%d %H:%M:%S") -- 2011-12-04 14:59:12
 end
 
--- if not os.sleep and socket then
---     os.sleep = socket.sleep
--- end
+if not os.sleep then
 
-if not os.sleep then
     local socket = socket
+
     function os.sleep(n)
         if not socket then
             -- so we delay ... if os.sleep is really needed then one should also
@@ -573,101 +585,105 @@
         end
         socket.sleep(n)
     end
+
 end
 
--- print(os.which("inkscape.exe"))
--- print(os.which("inkscape"))
--- print(os.which("gs.exe"))
--- print(os.which("ps2pdf"))
-
 -- These are moved from core-con.lua (as I needed them elsewhere).
 
-local function isleapyear(year) -- timed for bram's cs practicum
- -- return (year % 400 == 0) or (year % 100 ~= 0 and year % 4 == 0) -- 3:4:1600:1900 = 9.9 : 8.2 : 5.0 :  6.8 (29.9)
-    return (year % 4 == 0) and (year % 100 ~= 0 or year % 400 == 0) -- 3:4:1600:1900 = 5.1 : 6.5 : 8.1 : 10.2 (29.9)
- -- return (year % 4 == 0) and (year % 400 == 0 or year % 100 ~= 0) -- 3:4:1600:1900 = 5.2 : 8.5 : 6.8 : 10.1 (30.6)
-end
+do
 
-os.isleapyear = isleapyear
+    local function isleapyear(year) -- timed for bram's cs practicum
+     -- return (year % 400 == 0) or (year % 100 ~= 0 and year % 4 == 0) -- 3:4:1600:1900 = 9.9 : 8.2 : 5.0 :  6.8 (29.9)
+        return (year % 4 == 0) and (year % 100 ~= 0 or year % 400 == 0) -- 3:4:1600:1900 = 5.1 : 6.5 : 8.1 : 10.2 (29.9)
+     -- return (year % 4 == 0) and (year % 400 == 0 or year % 100 ~= 0) -- 3:4:1600:1900 = 5.2 : 8.5 : 6.8 : 10.1 (30.6)
+    end
 
--- nicer:
---
--- local days = {
---     [false] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
---     [true]  = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
--- }
---
--- local function nofdays(year,month)
---     return days[isleapyear(year)][month]
---     return month == 2 and isleapyear(year) and 29 or days[month]
--- end
---
--- more efficient:
+    os.isleapyear = isleapyear
 
-local days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+    -- nicer:
+    --
+    -- local days = {
+    --     [false] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+    --     [true]  = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+    -- }
+    --
+    -- local function nofdays(year,month)
+    --     return days[isleapyear(year)][month]
+    --     return month == 2 and isleapyear(year) and 29 or days[month]
+    -- end
+    --
+    -- more efficient:
 
-local function nofdays(year,month,day)
-    if not month then
-        return isleapyear(year) and 365 or 364
-    elseif not day then
-        return month == 2 and isleapyear(year) and 29 or days[month]
-    else
-        for i=1,month-1 do
-            day = day + days[i]
+    local days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+
+    local function nofdays(year,month,day)
+        if not month then
+            return isleapyear(year) and 365 or 364
+        elseif not day then
+            return month == 2 and isleapyear(year) and 29 or days[month]
+        else
+            for i=1,month-1 do
+                day = day + days[i]
+            end
+            if month > 2 and isleapyear(year) then
+                day = day + 1
+            end
+            return day
         end
-        if month > 2 and isleapyear(year) then
-            day = day + 1
-        end
-        return day
     end
-end
 
-os.nofdays = nofdays
+    os.nofdays = nofdays
 
-function os.weekday(day,month,year)
-    return date("%w",time { year = year, month = month, day = day }) + 1
-end
+    function os.weekday(day,month,year)
+        return date("%w",time { year = year, month = month, day = day }) + 1
+    end
 
-function os.validdate(year,month,day)
-    -- we assume that all three values are set
-    -- year is always ok, even if lua has a 1970 time limit
-    if month < 1 then
-        month = 1
-    elseif month > 12 then
-        month = 12
+    function os.validdate(year,month,day)
+        -- we assume that all three values are set
+        -- year is always ok, even if lua has a 1970 time limit
+        if month < 1 then
+            month = 1
+        elseif month > 12 then
+            month = 12
+        end
+        if day < 1 then
+            day = 1
+        else
+            local max = nofdays(year,month)
+            if day > max then
+                day = max
+            end
+        end
+        return year, month, day
     end
-    if day < 1 then
-        day = 1
-    else
-        local max = nofdays(year,month)
-        if day > max then
-            day = max
+
+    function os.date(fmt,...)
+        if not fmt then
+            -- otherwise differences between unix, mingw and msvc
+            fmt = "%Y-%m-%d %H:%M"
         end
+        return date(fmt,...)
     end
-    return year, month, day
-end
 
-function os.date(fmt,...)
-    if not fmt then
-        -- otherwise differences between unix, mingw and msvc
-        fmt = "%Y-%m-%d %H:%M"
-    end
-    return date(fmt,...)
 end
 
-local osexit   = os.exit
-local exitcode = nil
+do
 
-function os.setexitcode(code)
-    exitcode = code
-end
+    local osexit   = os.exit
+    local exitcode = nil
 
-function os.exit(c)
-    if exitcode ~= nil then
-        return osexit(exitcode)
+    function os.setexitcode(code)
+        exitcode = code
     end
-    if c ~= nil then
-        return osexit(c)
+
+    function os.exit(c)
+        if exitcode ~= nil then
+            return osexit(exitcode)
+        end
+        if c ~= nil then
+            return osexit(c)
+        end
+        return osexit()
     end
-    return osexit()
+
 end

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -346,3 +346,25 @@
 -- debug.showtraceback = showtraceback
 
 -- showtraceback()
+
+-- For now also here because we want it in mtxrun (taken from lmt file):
+
+if luac then
+
+    local show, dump = luac.print, string.dump
+
+    function luac.inspect(v)
+        if type(v) == "function" then
+            local ok, str = xpcall(dump,function() end,v)
+            if ok then
+                v = str
+            end
+        end
+        if type(v) == "string" then
+            show(v,true)
+        else
+            print(v)
+        end
+    end
+
+end

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -17,6 +17,10 @@
 --
 -- Upgraded for handling the somewhat more fax server templates.
 
+if utilities and utilities.json then
+    return json
+end
+
 local P, V, R, S, C, Cc, Cs, Ct, Cf, Cg = lpeg.P, lpeg.V, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cf, lpeg.Cg
 local lpegmatch = lpeg.match
 local format, gsub = string.format, string.gsub
@@ -265,6 +269,8 @@
                             k = lpegmatch(escaper,k) or k
                             v = lpegmatch(escaper,v) or v
                             n = n + 1 t[n] = f_key_val_str(depth,k,v)
+                        elseif i > 1 then
+                            n = n - 1
                         end
                     elseif tv == "table" then
                         local l = #v
@@ -280,6 +286,9 @@
                             end
                         elseif next(v) then
                             tojsonpp(v,k,depth,level+1,0)
+                        elseif i > 1 then
+                            n = n - 1
+                            -- we don't know if we have a hash or string
                         end
                     elseif tv == "boolean" then
                         if tk == "number" then
@@ -297,6 +306,8 @@
                             else
                                 t[n] = f_key_val_nop(depth,k)
                             end
+                        elseif i > 1 then
+                            n = n - 1
                         end
                     else
                         if tk == "number" then
@@ -306,6 +317,8 @@
                             k = lpegmatch(escaper,k) or k
                             n = n + 1
                             t[n] = f_key_val_null(depth,k)
+                        elseif i > 1 then
+                            n = n - 1
                         end
                     end
                 end
@@ -440,4 +453,8 @@
 -- inspect(l)
 -- print(s==l.s)
 
-return json
+-- if not package.loaded.json then
+--     package.loaded.json = json
+-- end
+
+-- return json

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -55,9 +55,13 @@
 
 local escape, left, right = P("\\"), P('{'), P('}')
 
+-- lpegpatterns.balanced = P {
+--     [1] = ((escape * (left+right)) + (1 - (left+right)) + V(2))^0,
+--     [2] = left * V(1) * right
+-- }
 lpegpatterns.balanced = P {
-    [1] = ((escape * (left+right)) + (1 - (left+right)) + V(2))^0,
-    [2] = left * V(1) * right
+    ((escape * (left+right)) + (1 - (left+right)) + V(2))^0,
+    left * V(1) * right
 }
 
 local nestedbraces   = P { lbrace   * (nobrace   + V(1))^0 * rbrace }
@@ -67,11 +71,12 @@
 local argument       = Cs((lbrace/"") * ((nobrace + nestedbraces)^0) * (rbrace/""))
 local content        = (1-endofstring)^0
 
-lpegpatterns.nestedbraces  = nestedbraces  -- no capture
-lpegpatterns.nestedparents = nestedparents -- no capture
-lpegpatterns.nested        = nestedbraces  -- no capture
-lpegpatterns.argument      = argument      -- argument after e.g. =
-lpegpatterns.content       = content       -- rest after e.g =
+lpegpatterns.nestedbraces   = nestedbraces   -- no capture
+lpegpatterns.nestedparents  = nestedparents  -- no capture
+lpegpatterns.nestedbrackets = nestedbrackets -- no capture
+lpegpatterns.nested         = nestedbraces   -- no capture
+lpegpatterns.argument       = argument       -- argument after e.g. =
+lpegpatterns.content        = content        -- rest after e.g =
 
 local value     = lbrace * C((nobrace + nestedbraces)^0) * rbrace
                 + C((nestedbraces + (1-comma))^0)
@@ -568,9 +573,9 @@
 -- "1","2","3","4"
 -- "5","6","7","8"
 -- ]]
---
+
 -- local mycsvsplitter = parsers.csvsplitter { numbers = true }
---
+
 -- local list = mycsvsplitter(crap) inspect(list)
 
 -- and this is a slightly patched version of a version posted by Philipp Gesang
@@ -617,12 +622,6 @@
 -- local list, names = mycsvsplitter(crap,true)   inspect(list) inspect(names)
 -- local list, names = mycsvsplitter(crap)        inspect(list) inspect(names)
 
--- parsers.stepper("1,7-",9,function(i) print(">>>",i) end)
--- parsers.stepper("1-3,7,8,9")
--- parsers.stepper("1-3,6,7",function(i) print(">>>",i) end)
--- parsers.stepper(" 1 : 3, ,7 ")
--- parsers.stepper("1:4,9:13,24:*",30)
-
 local function ranger(first,last,n,action)
     if not first then
         -- forget about it
@@ -639,7 +638,7 @@
     end
 end
 
-local cardinal    = lpegpatterns.cardinal / tonumber
+local cardinal    = (lpegpatterns.hexadecimal + lpegpatterns.cardinal) / tonumber
 local spacers     = lpegpatterns.spacer^0
 local endofstring = lpegpatterns.endofstring
 
@@ -650,14 +649,29 @@
                * Carg(1) * Carg(2) / ranger * S(", ")^0 )^1 * endofstring -- we're sort of strict (could do without endofstring)
 
 function parsers.stepper(str,n,action)
+    local ts = type(str)
     if type(n) == "function" then
-        lpegmatch(stepper,str,1,false,n or print)
-    else
+        if ts == "number" then
+            n(str)
+        elseif ts == "table" then
+            for i=1,#str do
+                n(str[i])
+            end
+        else
+            lpegmatch(stepper,str,1,false,n or print)
+        end
+    elseif ts == "string" then
         lpegmatch(stepper,str,1,n,action or print)
     end
 end
 
---
+-- parsers.stepper("1,7-",9,function(i) print(">>>",i) end)
+-- parsers.stepper("1-3,7,8,9")
+-- parsers.stepper("1-3,6,7",function(i) print(">>>",i) end)
+-- parsers.stepper(" 1 : 3, ,7 ")
+-- parsers.stepper("1:4,9:13,24:*",30)
+-- parsers.stepper(1,print)
+-- parsers.stepper({1,3,4},print)
 
 local pattern_math = Cs((P("%")/"\\percent " +  P("^")           * Cc("{") * lpegpatterns.integer * Cc("}") + anything)^0)
 local pattern_text = Cs((P("%")/"\\percent " + (P("^")/"\\high") * Cc("{") * lpegpatterns.integer * Cc("}") + anything)^0)
@@ -681,7 +695,7 @@
 local dummy   = function() end
 
 setmetatableindex(cache,function(t,k)
-    local separator = P(k)
+    local separator = S(k) -- was P
     local value     = (1-separator)^0
     local pattern   = spaces * C(value) * separator^0 * Cp()
     t[k] = pattern
@@ -801,11 +815,20 @@
         (              Cg(Cc("day")   * cardinal)
           *  S("-/") * Cg(Cc("month") * cardinal)
           *  S("-/") * Cg(Cc("year")  * p_year)
+        ) +
+        (              Cg(Cc("year")  * p_year)
+          *  S("-/") * Cg(Cc("month") * cardinal)
+        ) +
+        (              Cg(Cc("month")  * cardinal)
+          *  S("-/") * Cg(Cc("year")   * p_year)
         )
     )
-      *  P(" ")  * Cg(Cc("hour")   * cardinal)
+      *  (
+         P(" ")  * Cg(Cc("hour")   * cardinal)
       *  P(":")  * Cg(Cc("min")    * cardinal)
       * (P(":")  * Cg(Cc("sec")    * cardinal))^-1
+      + P(-1) )
+
 , rawset)
 
 lpegpatterns.splittime = pattern
@@ -814,6 +837,8 @@
     return lpegmatch(pattern,str)
 end
 
+-- inspect(parsers.totime("2019-03-05"))
+-- inspect(parsers.totime("2019-03-05 12:12:12"))
 -- print(os.time(parsers.totime("2019-03-05 12:12:12")))
 -- print(os.time(parsers.totime("2019/03/05 12:12:12")))
 -- print(os.time(parsers.totime("05-03-2019 12:12:12")))

Added: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sac.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sac.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sac.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -0,0 +1,582 @@
+if not modules then modules = { } end modules ['util-sac'] = {
+    version   = 1.001,
+    optimize  = true,
+    comment   = "companion to luat-lib.mkiv",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+-- experimental string access (some 3 times faster than file access when messing
+-- with bytes)
+
+local byte, sub = string.byte, string.sub
+local tonumber = tonumber
+
+utilities         = utilities or { }
+local streams     = { }
+utilities.streams = streams
+
+function streams.open(filename,zerobased)
+    local f = filename and io.loaddata(filename)
+    if f then
+        return { f, 1, #f, zerobased or false }
+    end
+end
+
+function streams.openstring(f,zerobased)
+    if f then
+        return { f, 1, #f, zerobased or false }
+    end
+end
+
+function streams.getstring(f)
+    if f then
+        return f[1]
+    end
+end
+
+function streams.close()
+    -- dummy
+end
+
+function streams.size(f)
+    return f and f[3] or 0
+end
+
+streams.getsize = streams.size
+
+function streams.setposition(f,i)
+    if f[4] then
+        -- zerobased
+        if i <= 0 then
+            f[2] = 1
+        else
+            f[2] = i + 1
+        end
+    else
+        if i <= 1 then
+            f[2] = 1
+        else
+            f[2] = i
+        end
+    end
+end
+
+function streams.getposition(f)
+    if f[4] then
+        -- zerobased
+        return f[2] - 1
+    else
+        return f[2]
+    end
+end
+
+function streams.look(f,n,chars)
+    local b = f[2]
+    local e = b + n - 1
+    if chars then
+        return sub(f[1],b,e)
+    else
+        return byte(f[1],b,e)
+    end
+end
+
+function streams.skip(f,n)
+    f[2] = f[2] + n
+end
+
+function streams.readbyte(f)
+    local i = f[2]
+    f[2] = i + 1
+    return byte(f[1],i)
+end
+
+function streams.readbytes(f,n)
+    local i = f[2]
+    local j = i + n
+    f[2] = j
+    return byte(f[1],i,j-1)
+end
+
+function streams.readbytetable(f,n)
+    local i = f[2]
+    local j = i + n
+    f[2] = j
+    return { byte(f[1],i,j-1) }
+end
+
+function streams.skipbytes(f,n)
+    f[2] = f[2] + n
+end
+
+function streams.readchar(f)
+    local i = f[2]
+    f[2] = i + 1
+    return sub(f[1],i,i)
+end
+
+function streams.readstring(f,n)
+    local i = f[2]
+    local j = i + n
+    f[2] = j
+    return sub(f[1],i,j-1)
+end
+
+function streams.readinteger1(f)  -- one byte
+    local i = f[2]
+    f[2] = i + 1
+    local n = byte(f[1],i)
+    if n  >= 0x80 then
+        return n - 0x100
+    else
+        return n
+    end
+end
+
+streams.readcardinal1 = streams.readbyte  -- one byte
+streams.readcardinal  = streams.readcardinal1
+streams.readinteger   = streams.readinteger1
+
+function streams.readcardinal2(f)
+    local i = f[2]
+    local j = i + 1
+    f[2] = j + 1
+    local a, b = byte(f[1],i,j)
+    return 0x100 * a + b
+end
+
+function streams.readcardinal2le(f)
+    local i = f[2]
+    local j = i + 1
+    f[2] = j + 1
+    local b, a = byte(f[1],i,j)
+    return 0x100 * a + b
+end
+
+function streams.readinteger2(f)
+    local i = f[2]
+    local j = i + 1
+    f[2] = j + 1
+    local a, b = byte(f[1],i,j)
+    if a >= 0x80 then
+        return 0x100 * a + b - 0x10000
+    else
+        return 0x100 * a + b
+    end
+end
+
+function streams.readinteger2le(f)
+    local i = f[2]
+    local j = i + 1
+    f[2] = j + 1
+    local b, a = byte(f[1],i,j)
+    if a >= 0x80 then
+        return 0x100 * a + b - 0x10000
+    else
+        return 0x100 * a + b
+    end
+end
+
+function streams.readcardinal3(f)
+    local i = f[2]
+    local j = i + 2
+    f[2] = j + 1
+    local a, b, c = byte(f[1],i,j)
+    return 0x10000 * a + 0x100 * b + c
+end
+
+function streams.readcardinal3le(f)
+    local i = f[2]
+    local j = i + 2
+    f[2] = j + 1
+    local c, b, a = byte(f[1],i,j)
+    return 0x10000 * a + 0x100 * b + c
+end
+
+function streams.readinteger3(f)
+    local i = f[2]
+    local j = i + 3
+    f[2] = j + 1
+    local a, b, c = byte(f[1],i,j)
+    if a >= 0x80 then
+        return 0x10000 * a + 0x100 * b + c - 0x1000000
+    else
+        return 0x10000 * a + 0x100 * b + c
+    end
+end
+
+function streams.readinteger3le(f)
+    local i = f[2]
+    local j = i + 3
+    f[2] = j + 1
+    local c, b, a = byte(f[1],i,j)
+    if a >= 0x80 then
+        return 0x10000 * a + 0x100 * b + c - 0x1000000
+    else
+        return 0x10000 * a + 0x100 * b + c
+    end
+end
+
+function streams.readcardinal4(f)
+    local i = f[2]
+    local j = i + 3
+    f[2] = j + 1
+    local a, b, c, d = byte(f[1],i,j)
+    return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
+end
+
+function streams.readcardinal4le(f)
+    local i = f[2]
+    local j = i + 3
+    f[2] = j + 1
+    local d, c, b, a = byte(f[1],i,j)
+    return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
+end
+
+function streams.readinteger4(f)
+    local i = f[2]
+    local j = i + 3
+    f[2] = j + 1
+    local a, b, c, d = byte(f[1],i,j)
+    if a >= 0x80 then
+        return 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000
+    else
+        return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
+    end
+end
+
+function streams.readinteger4le(f)
+    local i = f[2]
+    local j = i + 3
+    f[2] = j + 1
+    local d, c, b, a = byte(f[1],i,j)
+    if a >= 0x80 then
+        return 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000
+    else
+        return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
+    end
+end
+
+function streams.readfixed2(f)
+    local i = f[2]
+    local j = i + 1
+    f[2] = j + 1
+    local n1, n2 = byte(f[1],i,j)
+    if n1 >= 0x80 then
+        n1 = n1 - 0x100
+    end
+    return n1 + n2/0xFF
+end
+
+function streams.readfixed4(f)
+    local i = f[2]
+    local j = i + 3
+    f[2] = j + 1
+    local a, b, c, d = byte(f[1],i,j)
+    local n1 = 0x100 * a + b
+    local n2 = 0x100 * c + d
+    if n1 >= 0x8000 then
+        n1 = n1 - 0x10000
+    end
+    return n1 + n2/0xFFFF
+end
+
+if bit32 then
+
+    local extract = bit32.extract
+    local band    = bit32.band
+
+    function streams.read2dot14(f)
+        local i = f[2]
+        local j = i + 1
+        f[2] = j + 1
+        local a, b = byte(f[1],i,j)
+        if a >= 0x80 then
+            local n = -(0x100 * a + b)
+            return - (extract(n,14,2) + (band(n,0x3FFF) / 16384.0))
+        else
+            local n =   0x100 * a + b
+            return   (extract(n,14,2) + (band(n,0x3FFF) / 16384.0))
+        end
+    end
+
+end
+
+function streams.skipshort(f,n)
+    f[2] = f[2] + 2*(n or 1)
+end
+
+function streams.skiplong(f,n)
+    f[2] = f[2] + 4*(n or 1)
+end
+
+if sio and sio.readcardinal2 then
+
+    local readcardinal1  = sio.readcardinal1
+    local readcardinal2  = sio.readcardinal2
+    local readcardinal3  = sio.readcardinal3
+    local readcardinal4  = sio.readcardinal4
+    local readinteger1   = sio.readinteger1
+    local readinteger2   = sio.readinteger2
+    local readinteger3   = sio.readinteger3
+    local readinteger4   = sio.readinteger4
+    local readfixed2     = sio.readfixed2
+    local readfixed4     = sio.readfixed4
+    local read2dot14     = sio.read2dot14
+    local readbytes      = sio.readbytes
+    local readbytetable  = sio.readbytetable
+
+    function streams.readcardinal1(f)
+        local i = f[2]
+        f[2] = i + 1
+        return readcardinal1(f[1],i)
+    end
+    function streams.readcardinal2(f)
+        local i = f[2]
+        f[2] = i + 2
+        return readcardinal2(f[1],i)
+    end
+    function streams.readcardinal3(f)
+        local i = f[2]
+        f[2] = i + 3
+        return readcardinal3(f[1],i)
+    end
+    function streams.readcardinal4(f)
+        local i = f[2]
+        f[2] = i + 4
+        return readcardinal4(f[1],i)
+    end
+    function streams.readinteger1(f)
+        local i = f[2]
+        f[2] = i + 1
+        return readinteger1(f[1],i)
+    end
+    function streams.readinteger2(f)
+        local i = f[2]
+        f[2] = i + 2
+        return readinteger2(f[1],i)
+    end
+    function streams.readinteger3(f)
+        local i = f[2]
+        f[2] = i + 3
+        return readinteger3(f[1],i)
+    end
+    function streams.readinteger4(f)
+        local i = f[2]
+        f[2] = i + 4
+        return readinteger4(f[1],i)
+    end
+    function streams.readfixed2(f) -- needs recent luatex
+        local i = f[2]
+        f[2] = i + 2
+        return readfixed2(f[1],i)
+    end
+    function streams.readfixed4(f) -- needs recent luatex
+        local i = f[2]
+        f[2] = i + 4
+        return readfixed4(f[1],i)
+    end
+    function streams.read2dot14(f)
+        local i = f[2]
+        f[2] = i + 2
+        return read2dot14(f[1],i)
+    end
+    function streams.readbytes(f,n)
+        local i = f[2]
+        local s = f[3]
+        local p = i + n
+        if p > s then
+            f[2] = s + 1
+        else
+            f[2] = p
+        end
+        return readbytes(f[1],i,n)
+    end
+    function streams.readbytetable(f,n)
+        local i = f[2]
+        local s = f[3]
+        local p = i + n
+        if p > s then
+            f[2] = s + 1
+        else
+            f[2] = p
+        end
+        return readbytetable(f[1],i,n)
+    end
+
+    streams.readbyte       = streams.readcardinal1
+    streams.readsignedbyte = streams.readinteger1
+    streams.readcardinal   = streams.readcardinal1
+    streams.readinteger    = streams.readinteger1
+
+end
+
+if sio and sio.readcardinaltable then
+
+    local readcardinaltable = sio.readcardinaltable
+    local readintegertable  = sio.readintegertable
+
+    function utilities.streams.readcardinaltable(f,n,b)
+        local i = f[2]
+        local s = f[3]
+        local p = i + n * b
+        if p > s then
+            f[2] = s + 1
+        else
+            f[2] = p
+        end
+        return readcardinaltable(f[1],i,n,b)
+    end
+
+    function utilities.streams.readintegertable(f,n,b)
+        local i = f[2]
+        local s = f[3]
+        local p = i +  n * b
+        if p > s then
+            f[2] = s + 1
+        else
+            f[2] = p
+        end
+        return readintegertable(f[1],i,n,b)
+    end
+
+else
+
+    local readcardinal1 = streams.readcardinal1
+    local readcardinal2 = streams.readcardinal2
+    local readcardinal3 = streams.readcardinal3
+    local readcardinal4 = streams.readcardinal4
+
+    function streams.readcardinaltable(f,n,b)
+        local i = f[2]
+        local s = f[3]
+        local p = i + n * b
+        if p > s then
+            f[2] = s + 1
+        else
+            f[2] = p
+        end
+        local t = { }
+            if b == 1 then for i=1,n do t[i] = readcardinal1(f[1],i) end
+        elseif b == 2 then for i=1,n do t[i] = readcardinal2(f[1],i) end
+        elseif b == 3 then for i=1,n do t[i] = readcardinal3(f[1],i) end
+        elseif b == 4 then for i=1,n do t[i] = readcardinal4(f[1],i) end end
+        return t
+    end
+
+    local readinteger1 = streams.readinteger1
+    local readinteger2 = streams.readinteger2
+    local readinteger3 = streams.readinteger3
+    local readinteger4 = streams.readinteger4
+
+    function streams.readintegertable(f,n,b)
+        local i = f[2]
+        local s = f[3]
+        local p = i + n * b
+        if p > s then
+            f[2] = s + 1
+        else
+            f[2] = p
+        end
+        local t = { }
+            if b == 1 then for i=1,n do t[i] = readinteger1(f[1],i) end
+        elseif b == 2 then for i=1,n do t[i] = readinteger2(f[1],i) end
+        elseif b == 3 then for i=1,n do t[i] = readinteger3(f[1],i) end
+        elseif b == 4 then for i=1,n do t[i] = readinteger4(f[1],i) end end
+        return t
+    end
+
+end
+
+-- For practical reasons we put this here. It's less efficient but ok when we don't
+-- have much access.
+
+do
+
+    local files = utilities.files
+
+    if files then
+
+        local openfile     = files.open
+        local openstream   = streams.open
+        local openstring   = streams.openstring
+
+        local setmetatable = setmetatable
+
+        function io.newreader(str,method)
+            local f, m
+            if method == "string" then
+                f = openstring(str,true)
+                m = streams
+            elseif method == "stream" then
+                f = openstream(str,true)
+                m = streams
+            else
+                f = openfile(str,"rb")
+                m = files
+            end
+            if f then
+                local t = { }
+                setmetatable(t, {
+                    __index = function(t,k)
+                        local r = m[k]
+                        if k == "close" then
+                            -- maybe use __toclose
+                            if f then
+                                m.close(f)
+                                f = nil
+                            end
+                            return function() end
+                        elseif r then
+                            local v = function(_,a,b) return r(f,a,b) end
+                            t[k] = v
+                            return v
+                        else
+                            print("unknown key",k)
+                        end
+                    end
+                } )
+                return t
+            end
+        end
+
+    end
+
+end
+
+if bit32 and not streams.tocardinal1 then
+
+    local extract = bit32.extract
+    local char    = string.char
+
+             streams.tocardinal1           = char
+    function streams.tocardinal2(n)   return char(extract( 8,8),extract( 0,8)) end
+    function streams.tocardinal3(n)   return char(extract(16,8),extract( 8,8),extract(0,8)) end
+    function streams.tocardinal4(n)   return char(extract(24,8),extract(16,8),extract(8,8),extract(0,8)) end
+
+             streams.tocardinal1le         = char
+    function streams.tocardinal2le(n) return char(extract(0,8),extract(8,8)) end
+    function streams.tocardinal3le(n) return char(extract(0,8),extract(8,8),extract(16,8)) end
+    function streams.tocardinal4le(n) return char(extract(0,8),extract(8,8),extract(16,8),extract(24,8)) end
+
+end
+
+if not streams.readcstring then
+
+    local readchar = streams.readchar
+    local concat   = table.concat
+
+    function streams.readcstring(f)
+        local t = { }
+        while true do
+            local c = readchar(f)
+            if c and c ~= "\0" then
+                t[#t+1] = c
+            else
+                return concat(t)
+            end
+        end
+    end
+
+end


Property changes on: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sac.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -10,12 +10,13 @@
 utilities.strings = utilities.strings or { }
 local strings     = utilities.strings
 
-local format, gsub, rep, sub, find = string.format, string.gsub, string.rep, string.sub, string.find
+local format, gsub, rep, sub, find, char = string.format, string.gsub, string.rep, string.sub, string.find, string.char
 local load, dump = load, string.dump
 local tonumber, type, tostring, next, setmetatable = tonumber, type, tostring, next, setmetatable
 local unpack, concat = table.unpack, table.concat
 local P, V, C, S, R, Ct, Cs, Cp, Carg, Cc = lpeg.P, lpeg.V, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cs, lpeg.Cp, lpeg.Carg, lpeg.Cc
 local patterns, lpegmatch = lpeg.patterns, lpeg.match
+local tsplitat = lpeg.tsplitat
 local utfchar, utfbyte, utflen = utf.char, utf.byte, utf.len
 
 ----- loadstripped = utilities.lua.loadstripped
@@ -622,7 +623,7 @@
 return function(%s) return %s end
 ]]
 
--- this might move
+-- We only use fast serialize in controlled cases.
 
 local pattern = Cs(Cc('"') * (
     (1-S('"\\\n\r'))^1
@@ -632,6 +633,16 @@
   + P('\r') / '\\r'
 )^0 * Cc('"'))
 
+-- -- I need to do more experiments with this:
+--
+-- local pattern = Cs(Cc('"') * (
+--     (1-S('"\\\n\r'))^1
+--   + P('"')  / '\\034'
+--   + P('\\') / '\\092'
+--   + P('\n') / '\\013'
+--   + P('\r') / '\\010'
+-- )^0 * Cc('"'))
+
 patterns.escapedquotes = pattern
 
 function string.escapedquotes(s)
@@ -638,6 +649,27 @@
     return lpegmatch(pattern,s)
 end
 
+local pattern = (1 - P("\\"))^1 ; pattern = Cs (
+    pattern
+ * ( (P("\\") / "" * (digit^-3 / function(s) return char(tonumber(s)) end)) + pattern )^1
+)
+
+patterns.unescapedquotes = pattern
+
+function string.unescapedquotes(s)
+    return lpegmatch(pattern,s) or s
+end
+
+-- function string.longifneeded(s)
+--     if find(s,'["\\\n\r]') then
+--         return "[===[" .. s .. "]===]"
+--     else
+--         return '"' .. s ..'"'
+--     end
+-- end
+
+string.texnewlines = lpeg.replacer(patterns.newline,"\r",true)
+
 -- print(string.escapedquotes('1\\23\n"'))
 
 -- but for now here
@@ -1476,7 +1508,7 @@
 
 if not string.explode then
 
-    local tsplitat = lpeg.tsplitat
+ -- local tsplitat = lpeg.tsplitat
 
     local p_utf   = patterns.utf8character
     local p_check = C(p_utf) * (P("+") * Cc(true))^0
@@ -1499,3 +1531,24 @@
     end
 
 end
+
+
+do
+
+    local p_whitespace = patterns.whitespace^1
+
+    local cache = setmetatable({ }, { __index = function(t,k)
+        local p = tsplitat(p_whitespace * P(k) * p_whitespace)
+        local v = function(s)
+            return lpegmatch(p,s)
+        end
+        t[k] = v
+        return v
+    end })
+
+    function string.wordsplitter(s)
+        return cache[s]
+    end
+
+end
+

Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -417,9 +417,9 @@
 
 else
 
-    local f_v = formatters["[%q]=%q,"]
-    local f_t = formatters["[%q]="]
-    local f_q = formatters["%q,"]
+ -- local f_v = formatters["[%q]=%q,"]
+ -- local f_t = formatters["[%q]="]
+ -- local f_q = formatters["%q,"]
 
     function table.fastserialize(t,prefix) -- todo, move local function out
         local r = { type(prefix) == "string" and prefix or "return" }
@@ -720,6 +720,7 @@
 
     local t    -- = { }
     local n       = 1
+ -- local m       = 0 -- no gain
     local unknown = false
 
     local function do_serialize(root,name,depth,level,indexed)
@@ -850,6 +851,12 @@
                         n = n + 1 t[n] = f_key_str_value_str(depth,tostring(k),tostring(v))
                     end
                 end
+             -- if n > 100000 then -- no gain
+             --     local k = m + 1
+             --     t[k] = concat(t,"\n",k,n)
+             --     n = k
+             --     m = k
+             -- end
             end
         end
         if level > 0 then
@@ -898,6 +905,7 @@
     n = n + 1
     t[n] = f_table_finish()
     return concat(t,"\n")
+ -- return concat(t,"\n",1,n) -- no gain
 end
 
 table.serialize = serialize
@@ -970,3 +978,43 @@
 --         return remove(t,random(1,n))
 --     end
 -- end
+
+function combine(target,source)
+    -- no copy so if that is needed one needs to deepcopy source first
+    if target then
+        for k, v in next, source do
+            if type(v) == "table" then
+               target[k] = combine(target[k],source[k])
+            else
+               target[k] = v
+            end
+        end
+        return target
+    else
+        return source
+    end
+end
+
+table.combine = combine
+
+-- If needed we can add something (some discussion on the list but I'm not sure if
+-- it makes sense because merging such mixed tables is quite unusual.
+--
+-- function table.himerged(...)
+--     local result = { }
+--     local r      = 0
+--     for i=1,select("#",...) do
+--         local s = select(i,...)
+--         if s then
+--             for k, v in next, s do
+--                 if type(k) == "number"  then
+--                     r = r + 1
+--                     result[r] = v
+--                 else
+--                     result[k] = v
+--                 end
+--             end
+--         end
+--     end
+--     return result
+-- end

Added: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-zip.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-zip.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-zip.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -0,0 +1,684 @@
+if not modules then modules = { } end modules ['util-zip'] = {
+    version   = 1.001,
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+-- This module is mostly meant for relative simple zip and unzip tasks. We can read
+-- and write zip files but with limitations. Performance is quite good and it makes
+-- us independent of zip tools, which (for some reason) are not always installed.
+--
+-- This is an lmtx module and at some point will be lmtx only but for a while we
+-- keep some hybrid functionality.
+
+local type, tostring, tonumber = type, tostring, tonumber
+local sort, concat = table.sort, table.concat
+
+local find, format, sub, gsub = string.find, string.format, string.sub, string.gsub
+local osdate, ostime, osclock = os.date, os.time, os.clock
+local ioopen = io.open
+local loaddata, savedata = io.loaddata, io.savedata
+local filejoin, isdir, dirname, mkdirs = file.join, lfs.isdir, file.dirname, dir.mkdirs
+local suffix, suffixes = file.suffix, file.suffixes
+local openfile = io.open
+
+gzip = gzip or { } -- so in luatex we keep the old ones too
+
+if not zlib then
+    zlib = xzip    -- in luametatex we shadow the old one
+elseif not xzip then
+    xzip = zlib
+end
+
+local files         = utilities.files
+local openfile      = files.open
+local closefile     = files.close
+local readstring    = files.readstring
+local readcardinal2 = files.readcardinal2le
+local readcardinal4 = files.readcardinal4le
+local setposition   = files.setposition
+local getposition   = files.getposition
+
+local band          = bit32.band
+local rshift        = bit32.rshift
+local lshift        = bit32.lshift
+
+local zlibdecompress     = zlib.decompress
+local zlibdecompresssize = zlib.decompresssize
+local zlibchecksum       = zlib.crc32
+
+if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
+    local cs = zlibchecksum
+    zlibchecksum = function(str,n) return cs(n or 0, str) end
+end
+
+local decompress     = function(source)            return zlibdecompress    (source,-15)            end -- auto
+local decompresssize = function(source,targetsize) return zlibdecompresssize(source,targetsize,-15) end -- auto
+local calculatecrc   = function(buffer,initial)    return zlibchecksum      (initial or 0,buffer)   end
+
+local zipfiles      = { }
+utilities.zipfiles  = zipfiles
+
+local openzipfile, closezipfile, unzipfile, foundzipfile, getziphash, getziplist  do
+
+    function openzipfile(name)
+        return {
+            name   = name,
+            handle = openfile(name,0),
+        }
+    end
+
+    local function collect(z)
+        if not z.list then
+            local list     = { }
+            local hash     = { }
+            local position = 0
+            local index    = 0
+            local handle   = z.handle
+            while true do
+                setposition(handle,position)
+                local signature = readstring(handle,4)
+                if signature == "PK\3\4" then
+                    -- [local file header 1]
+                    -- [encryption header 1]
+                    -- [file data 1]
+                    -- [data descriptor 1]
+                    local version      = readcardinal2(handle)
+                    local flag         = readcardinal2(handle)
+                    local method       = readcardinal2(handle)
+                    local filetime     = readcardinal2(handle)
+                    local filedate     = readcardinal2(handle)
+                    local crc32        = readcardinal4(handle)
+                    local compressed   = readcardinal4(handle)
+                    local uncompressed = readcardinal4(handle)
+                    local namelength   = readcardinal2(handle)
+                    local extralength  = readcardinal2(handle)
+                    local filename     = readstring(handle,namelength)
+                    local descriptor   = band(flag,8) ~= 0
+                    local encrypted    = band(flag,1) ~= 0
+                    local acceptable   = method == 0 or method == 8
+                    -- 30 bytes of header including the signature
+                    local skipped      = 0
+                    local size         = 0
+                    if encrypted then
+                        size = readcardinal2(handle)
+                        skipbytes(size)
+                        skipped = skipped + size + 2
+                        skipbytes(8)
+                        skipped = skipped + 8
+                        size = readcardinal2(handle)
+                        skipbytes(size)
+                        skipped = skipped + size + 2
+                        size = readcardinal4(handle)
+                        skipbytes(size)
+                        skipped = skipped + size + 4
+                        size = readcardinal2(handle)
+                        skipbytes(size)
+                        skipped = skipped + size + 2
+                    end
+                    position = position + 30 + namelength + extralength + skipped
+                    if descriptor then
+                        setposition(handle,position + compressed)
+                        crc32        = readcardinal4(handle)
+                        compressed   = readcardinal4(handle)
+                        uncompressed = readcardinal4(handle)
+                    end
+                    if acceptable then
+                        index = index + 1
+                        local data = {
+                            filename     = filename,
+                            index        = index,
+                            position     = position,
+                            method       = method,
+                            compressed   = compressed,
+                            uncompressed = uncompressed,
+                            crc32        = crc32,
+                            encrypted    = encrypted,
+                        }
+                        hash[filename] = data
+                        list[index]    = data
+                    else
+                        -- maybe a warning when encrypted
+                    end
+                    position = position + compressed
+                else
+                    break
+                end
+                z.list = list
+                z.hash = hash
+            end
+        end
+    end
+
+    function getziplist(z)
+        local list = z.list
+        if not list then
+            collect(z)
+        end
+        return z.list
+    end
+
+    function getziphash(z)
+        local hash = z.hash
+        if not hash then
+            collect(z)
+        end
+        return z.hash
+    end
+
+    function foundzipfile(z,name)
+        return getziphash(z)[name]
+    end
+
+    function closezipfile(z)
+        local f = z.handle
+        if f then
+            closefile(f)
+            z.handle = nil
+        end
+    end
+
+    function unzipfile(z,filename,check)
+        local hash = z.hash
+        if not hash then
+            hash = zipfiles.hash(z)
+        end
+        local data = hash[filename] -- normalize
+        if not data then
+            -- lower and cleanup
+            -- only name
+        end
+        if data then
+            local handle     = z.handle
+            local position   = data.position
+            local compressed = data.compressed
+            if compressed > 0 then
+                setposition(handle,position)
+                local result = readstring(handle,compressed)
+                if data.method == 8 then
+                    if decompresssize then
+                        result = decompresssize(result,data.uncompressed)
+                    else
+                        result = decompress(result)
+                    end
+                end
+                if check and data.crc32 ~= calculatecrc(result) then
+                    print("checksum mismatch")
+                    return ""
+                end
+                return result
+            else
+                return ""
+            end
+        end
+    end
+
+    zipfiles.open  = openzipfile
+    zipfiles.close = closezipfile
+    zipfiles.unzip = unzipfile
+    zipfiles.hash  = getziphash
+    zipfiles.list  = getziplist
+    zipfiles.found = foundzipfile
+
+end
+
+if xzip then -- flate then do
+
+    local writecardinal1 = files.writebyte
+    local writecardinal2 = files.writecardinal2le
+    local writecardinal4 = files.writecardinal4le
+
+    local logwriter      = logs.writer
+
+    local globpattern    = dir.globpattern
+--     local compress       = flate.flate_compress
+--     local checksum       = flate.update_crc32
+    local compress       = xzip.compress
+    local checksum       = xzip.crc32
+
+ -- local function fromdostime(dostime,dosdate)
+ --     return ostime {
+ --         year  = (dosdate >>  9) + 1980, -- 25 .. 31
+ --         month = (dosdate >>  5) & 0x0F, -- 21 .. 24
+ --         day   = (dosdate      ) & 0x1F, -- 16 .. 20
+ --         hour  = (dostime >> 11)       , -- 11 .. 15
+ --         min   = (dostime >>  5) & 0x3F, --  5 .. 10
+ --         sec   = (dostime      ) & 0x1F, --  0 ..  4
+ --     }
+ -- end
+ --
+ -- local function todostime(time)
+ --     local t = osdate("*t",time)
+ --     return
+ --         ((t.year - 1980) <<  9) + (t.month << 5) +  t.day,
+ --          (t.hour         << 11) + (t.min   << 5) + (t.sec >> 1)
+ -- end
+
+    local function fromdostime(dostime,dosdate)
+        return ostime {
+            year  =      rshift(dosdate, 9) + 1980,  -- 25 .. 31
+            month = band(rshift(dosdate, 5),  0x0F), -- 21 .. 24
+            day   = band(      (dosdate   ),  0x1F), -- 16 .. 20
+            hour  = band(rshift(dostime,11)       ), -- 11 .. 15
+            min   = band(rshift(dostime, 5),  0x3F), --  5 .. 10
+            sec   = band(      (dostime   ),  0x1F), --  0 ..  4
+        }
+    end
+
+    local function todostime(time)
+        local t = osdate("*t",time)
+        return
+            lshift(t.year - 1980, 9) + lshift(t.month,5) +        t.day,
+            lshift(t.hour       ,11) + lshift(t.min  ,5) + rshift(t.sec,1)
+    end
+
+    local function openzip(filename,level,comment,verbose)
+        local f = ioopen(filename,"wb")
+        if f then
+            return {
+                filename     = filename,
+                handle       = f,
+                list         = { },
+                level        = tonumber(level) or 3,
+                comment      = tostring(comment),
+                verbose      = verbose,
+                uncompressed = 0,
+                compressed   = 0,
+            }
+        end
+    end
+
+    local function writezip(z,name,data,level,time)
+        local f        = z.handle
+        local list     = z.list
+        local level    = tonumber(level) or z.level or 3
+        local method   = 8
+        local zipped   = compress(data,level)
+        local checksum = checksum(data)
+        local verbose  = z.verbose
+        --
+        if not zipped then
+            method = 0
+            zipped = data
+        end
+        --
+        local start        = f:seek()
+        local compressed   = #zipped
+        local uncompressed = #data
+        --
+        z.compressed   = z.compressed   + compressed
+        z.uncompressed = z.uncompressed + uncompressed
+        --
+        if verbose then
+            local pct = 100 * compressed/uncompressed
+            if pct >= 100 then
+                logwriter(format("%10i        %s",uncompressed,name))
+            else
+                logwriter(format("%10i  %02.1f  %s",uncompressed,pct,name))
+            end
+        end
+        --
+        f:write("\x50\x4b\x03\x04") -- PK..  0x04034b50
+        --
+        writecardinal2(f,0)            -- minimum version
+        writecardinal2(f,0)            -- flag
+        writecardinal2(f,method)       -- method
+        writecardinal2(f,0)            -- time
+        writecardinal2(f,0)            -- date
+        writecardinal4(f,checksum)     -- crc32
+        writecardinal4(f,compressed)   -- compressed
+        writecardinal4(f,uncompressed) -- uncompressed
+        writecardinal2(f,#name)        -- namelength
+        writecardinal2(f,0)            -- extralength
+        --
+        f:write(name)                  -- name
+        f:write(zipped)
+        --
+        list[#list+1] = { #zipped, #data, name, checksum, start, time or 0 }
+    end
+
+    local function closezip(z)
+        local f       = z.handle
+        local list    = z.list
+        local comment = z.comment
+        local verbose = z.verbose
+        local count   = #list
+        local start   = f:seek()
+        --
+        for i=1,count do
+            local l = list[i]
+            local compressed   = l[1]
+            local uncompressed = l[2]
+            local name         = l[3]
+            local checksum     = l[4]
+            local start        = l[5]
+            local time         = l[6]
+            local date, time   = todostime(time)
+            f:write('\x50\x4b\x01\x02')
+            writecardinal2(f,0)            -- version made by
+            writecardinal2(f,0)            -- version needed to extract
+            writecardinal2(f,0)            -- flags
+            writecardinal2(f,8)            -- method
+            writecardinal2(f,time)         -- time
+            writecardinal2(f,date)         -- date
+            writecardinal4(f,checksum)     -- crc32
+            writecardinal4(f,compressed)   -- compressed
+            writecardinal4(f,uncompressed) -- uncompressed
+            writecardinal2(f,#name)        -- namelength
+            writecardinal2(f,0)            -- extralength
+            writecardinal2(f,0)            -- commentlength
+            writecardinal2(f,0)            -- nofdisks -- ?
+            writecardinal2(f,0)            -- internal attr (type)
+            writecardinal4(f,0)            -- external attr (mode)
+            writecardinal4(f,start)        -- local offset
+            f:write(name)                  -- name
+        end
+        --
+        local stop = f:seek()
+        local size = stop - start
+        --
+        f:write('\x50\x4b\x05\x06')
+        writecardinal2(f,0)            -- disk
+        writecardinal2(f,0)            -- disks
+        writecardinal2(f,count)        -- entries
+        writecardinal2(f,count)        -- entries
+        writecardinal4(f,size)         -- dir size
+        writecardinal4(f,start)        -- dir offset
+        if type(comment) == "string" and comment ~= "" then
+            writecardinal2(f,#comment) -- comment length
+            f:write(comment)           -- comemnt
+        else
+            writecardinal2(f,0)
+        end
+        --
+        if verbose then
+            local compressed   = z.compressed
+            local uncompressed = z.uncompressed
+            local filename     = z.filename
+            --
+            local pct = 100 * compressed/uncompressed
+            logwriter("")
+            if pct >= 100 then
+                logwriter(format("%10i        %s",uncompressed,filename))
+            else
+                logwriter(format("%10i  %02.1f  %s",uncompressed,pct,filename))
+            end
+        end
+        --
+        f:close()
+    end
+
+    local function zipdir(zipname,path,level,verbose)
+        if type(zipname) == "table" then
+            verbose = zipname.verbose
+            level   = zipname.level
+            path    = zipname.path
+            zipname = zipname.zipname
+        end
+        if not zipname or zipname == "" then
+            return
+        end
+        if not path or path == "" then
+            path = "."
+        end
+        if not isdir(path) then
+            return
+        end
+        path = gsub(path,"\\+","/")
+        path = gsub(path,"/+","/")
+        local list  = { }
+        local count = 0
+        globpattern(path,"",true,function(name,size,time)
+            count = count + 1
+            list[count] = { name, time }
+        end)
+        sort(list,function(a,b)
+            return a[1] < b[1]
+        end)
+        local zipf = openzip(zipname,level,comment,verbose)
+        if zipf then
+            local p = #path + 2
+            for i=1,count do
+                local li   = list[i]
+                local name = li[1]
+                local time = li[2]
+                local data = loaddata(name)
+                local name = sub(name,p,#name)
+                writezip(zipf,name,data,level,time,verbose)
+            end
+            closezip(zipf)
+        end
+    end
+
+    local function unzipdir(zipname,path,verbose)
+        if type(zipname) == "table" then
+            verbose = zipname.verbose
+            path    = zipname.path
+            zipname = zipname.zipname
+        end
+        if not zipname or zipname == "" then
+            return
+        end
+        if not path or path == "" then
+            path = "."
+        end
+        local z = openzipfile(zipname)
+        if z then
+            local list = getziplist(z)
+            if list then
+                local total = 0
+                local count = #list
+                local step  = number.idiv(count,10)
+                local done  = 0
+                local steps = verbose == "steps"
+                local time  = steps and osclock()
+                for i=1,count do
+                    local l = list[i]
+                    local n = l.filename
+                    local d = unzipfile(z,n) -- true for check
+                    if d then
+                        local p = filejoin(path,n)
+                        if mkdirs(dirname(p)) then
+                            if steps then
+                                total = total + #d
+                                done = done + 1
+                                if done >= step then
+                                    done = 0
+                                    logwriter(format("%4i files of %4i done, %10i bytes, %0.3f seconds",i,count,total,osclock()-time))
+                                end
+                            elseif verbose then
+                                logwriter(n)
+                            end
+                            savedata(p,d)
+                        end
+                    else
+                        logwriter(format("problem with file %s",n))
+                    end
+                end
+                if steps then
+                    logwriter(format("%4i files of %4i done, %10i bytes, %0.3f seconds",count,count,total,osclock()-time))
+                end
+                closezipfile(z)
+                return true
+            else
+                closezipfile(z)
+            end
+        end
+    end
+
+    zipfiles.zipdir   = zipdir
+    zipfiles.unzipdir = unzipdir
+
+end
+
+-- todo: compress/decompress that work with offset in string
+
+-- We only have a few official methods here:
+--
+--   local decompressed = gzip.load       (filename)
+--   local resultsize   = gzip.save       (filename,compresslevel)
+--   local compressed   = gzip.compress   (str,compresslevel)
+--   local decompressed = gzip.decompress (str)
+--   local iscompressed = gzip.compressed (str)
+--   local suffix, okay = gzip.suffix     (filename)
+--
+-- In LuaMetaTeX we have only xzip which implements a very few methods:
+--
+--   compress   (str,level,method,window,memory,strategy)
+--   decompress (str,window)
+--   adler32    (str,checksum)
+--   crc32      (str,checksum)
+
+local pattern   = "^\x1F\x8B\x08"
+local gziplevel = 3
+
+function gzip.suffix(filename)
+    local suffix, extra = suffixes(filename)
+    local gzipped = extra == "gz"
+    return suffix, gzipped
+end
+
+function gzip.compressed(s)
+    return s and find(s,pattern)
+end
+
+local getdecompressed
+local putcompressed
+
+if gzip.compress then
+
+    local gzipwindow = 15 + 16 -- +16: gzip, +32: gzip|zlib
+
+    local compress   = zlib.compress
+    local decompress = zlib.decompress
+
+    getdecompressed = function(str)
+        return decompress(str,gzipwindow) -- pass offset
+    end
+
+    putcompressed = function(str,level)
+        return compress(str,level or gziplevel,nil,gzipwindow)
+    end
+
+else
+
+    -- Special window values are: flate: -15, zlib: 15, gzip : -15
+
+    local gzipwindow = -15 -- miniz needs this
+    local identifier = "\x1F\x8B"
+
+    local compress      = zlib.compress
+    local decompress    = zlib.decompress
+    local zlibchecksum  = zlib.crc32
+
+    if not CONTEXTLMTXMODE or CONTEXTLMTXMODE == 0 then
+        local cs = zlibchecksum
+        zlibchecksum = function(str,n) return cs(n or 0, str) end
+    end
+
+    local streams       = utilities.streams
+    local openstream    = streams.openstring
+    local closestream   = streams.close
+    local getposition   = streams.getposition
+    local readbyte      = streams.readbyte
+    local readcardinal4 = streams.readcardinal4le
+    local readcardinal2 = streams.readcardinal2le
+    local readstring    = streams.readstring
+    local readcstring   = streams.readcstring
+    local skipbytes     = streams.skip
+
+    local tocardinal1   = streams.tocardinal1
+    local tocardinal4   = streams.tocardinal4le
+
+    getdecompressed = function(str)
+        local s = openstream(str)
+        local identifier  = readstring(s,2)
+        local method      = readbyte(s,1)
+        local flags       = readbyte(s,1)
+        local timestamp   = readcardinal4(s)
+        local compression = readbyte(s,1)
+        local operating   = readbyte(s,1)
+     -- local isjusttext  = (flags & 0x01 ~= 0) and true             or false
+     -- local extrasize   = (flags & 0x04 ~= 0) and readcardinal2(s) or 0
+     -- local filename    = (flags & 0x08 ~= 0) and readcstring(s)   or ""
+     -- local comment     = (flags & 0x10 ~= 0) and readcstring(s)   or ""
+     -- local checksum    = (flags & 0x02 ~= 0) and readcardinal2(s) or 0
+        local isjusttext  = band(flags,0x01) ~= 0 and true             or false
+        local extrasize   = band(flags,0x04) ~= 0 and readcardinal2(s) or 0
+        local filename    = band(flags,0x08) ~= 0 and readcstring(s)   or ""
+        local comment     = band(flags,0x10) ~= 0 and readcstring(s)   or ""
+        local checksum    = band(flags,0x02) ~= 0 and readcardinal2(s) or 0
+        local compressed  = readstring(s,#str)
+        local data = decompress(compressed,gzipwindow) -- pass offset
+        return data
+    end
+
+    putcompressed = function(str,level,originalname)
+        return concat {
+            identifier, -- 2 identifier
+            tocardinal1(0x08), -- 1 method
+            tocardinal1(0x08), -- 1 flags
+            tocardinal4(os.time()), -- 4 mtime
+            tocardinal1(0x02), -- 1 compression (2 or 4)
+            tocardinal1(0xFF), -- 1 operating
+            (originalname or "unknownname") .. "\0",
+            compress(str,level,nil,gzipwindow),
+            tocardinal4(zlibchecksum(str)), -- 4
+            tocardinal4(#str), -- 4
+        }
+    end
+
+end
+
+function gzip.load(filename)
+    local f = openfile(filename,"rb")
+    if not f then
+        -- invalid file
+    else
+        local data = f:read("*all")
+        f:close()
+        if data and data ~= "" then
+            if suffix(filename) == "gz" then
+                data = getdecompressed(data)
+            end
+            return data
+        end
+    end
+end
+
+function gzip.save(filename,data,level,originalname)
+    if suffix(filename) ~= "gz" then
+        filename = filename .. ".gz"
+    end
+    local f = openfile(filename,"wb")
+    if f then
+        data = putcompressed(data or "",level or gziplevel,originalname)
+        f:write(data)
+        f:close()
+        return #data
+    end
+end
+
+function gzip.compress(s,level)
+    if s and not find(s,pattern) then
+        if not level then
+            level = gziplevel
+        elseif level <= 0 then
+            return s
+        elseif level > 9 then
+            level = 9
+        end
+        return putcompressed(s,level or gziplevel) or s
+    end
+end
+
+function gzip.decompress(s)
+    if s and find(s,pattern) then
+        return getdecompressed(s)
+    else
+        return s
+    end
+end
+
+-- return zipfiles


Property changes on: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-zip.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua	2022-10-04 19:54:54 UTC (rev 64614)
+++ trunk/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua	2022-10-04 19:55:23 UTC (rev 64615)
@@ -25,8 +25,8 @@
 
 lualibs.module_info = {
   name          = "lualibs",
-  version       = "2.74",       --TAGVERSION
-    date        = "2021-05-20", --TAGDATE
+  version       = "2.75",       --TAGVERSION
+    date        = "2022-10-04", --TAGDATE
   description   = "ConTeXt Lua standard libraries.",
   author        = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang",
   copyright     = "PRAGMA ADE / ConTeXt Development Team",



More information about the tex-live-commits mailing list.