From 01fd63f17c80e81218b3d65f1455a62c411dc6ff Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg42.2a@gmail.com>
Date: Tue, 30 Apr 2013 11:55:11 +0200
Subject: sync with Context from 2013-04-30

---
 lualibs-dir.lua      |  23 +++++-
 lualibs-os.lua       |  16 +++-
 lualibs-package.lua  | 216 ++++++++++++++++++++++++++-------------------------
 lualibs-table.lua    |   9 +--
 lualibs-util-env.lua |  29 +++++++
 5 files changed, 177 insertions(+), 116 deletions(-)

diff --git a/lualibs-dir.lua b/lualibs-dir.lua
index 00cda38..3d0576e 100644
--- a/lualibs-dir.lua
+++ b/lualibs-dir.lua
@@ -10,7 +10,7 @@ if not modules then modules = { } end modules ['l-dir'] = {
 
 local type, select = type, select
 local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub
-local concat, insert, remove = table.concat, table.insert, table.remove
+local concat, insert, remove, unpack = table.concat, table.insert, table.remove, table.unpack
 local lpegmatch = lpeg.match
 
 local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V
@@ -447,3 +447,24 @@ function dir.pop()
     end
     return d
 end
+
+local function found(...) -- can have nil entries
+    for i=1,select("#",...) do
+        local path = select(i,...)
+        local kind = type(path)
+        if kind == "string" then
+            if isdir(path) then
+                return path
+            end
+        elseif kind == "table" then
+            -- here we asume no holes, i.e. an indexed table
+            local path = found(unpack(path))
+            if path then
+                return path
+            end
+        end
+    end
+ -- return nil -- if we want print("crappath") to show something
+end
+
+dir.found = found
diff --git a/lualibs-os.lua b/lualibs-os.lua
index 42f3e48..05ca0ac 100644
--- a/lualibs-os.lua
+++ b/lualibs-os.lua
@@ -452,8 +452,20 @@ 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
+-- if not os.sleep and socket then
+--     os.sleep = socket.sleep
+-- end
+
+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
+            -- be sure that socket can be found
+            socket = require("socket")
+        end
+        socket.sleep(n)
+    end
 end
 
 -- print(os.which("inkscape.exe"))
diff --git a/lualibs-package.lua b/lualibs-package.lua
index 7b82fa5..579fd39 100644
--- a/lualibs-package.lua
+++ b/lualibs-package.lua
@@ -30,8 +30,6 @@ local filejoin   = file and file.join        or function(path,name)   return pat
 local isreadable = file and file.is_readable or function(name)        local f = io.open(name) if f then f:close() return true end end
 local addsuffix  = file and file.addsuffix   or function(name,suffix) return name .. "." .. suffix end
 
---
-
 -- local separator, concatinator, placeholder, pathofexecutable, ignorebefore = string.match(package.config,"(.-)\n(.-)\n(.-)\n(.-)\n(.-)\n")
 --
 -- local config = {
@@ -42,8 +40,6 @@ local addsuffix  = file and file.addsuffix   or function(name,suffix) return nam
 --     ignorebefore     = ignorebefore,        -- - remove all before this when making lua_open
 -- }
 
---
-
 local function cleanpath(path) -- hm, don't we have a helper for this?
     return path
 end
@@ -54,16 +50,18 @@ local function lualibfile(name)
     return lpegmatch(pattern,name) or name
 end
 
+local offset = luarocks and 1 or 0 -- todo: also check other extras
+
 local helpers = package.helpers or {
     cleanpath  = cleanpath,
     lualibfile = lualibfile,
     trace      = false,
     report     = function(...) print(format(...)) end,
     builtin    = {
-        ["preload table"]       = searchers[1], -- special case, built-in libs
-        ["path specification"]  = searchers[2],
-        ["cpath specification"] = searchers[3],
-        ["all in one fallback"] = searchers[4], -- special case, combined libs
+        ["preload table"]       = searchers[1+offset], -- special case, built-in libs
+        ["path specification"]  = searchers[2+offset],
+        ["cpath specification"] = searchers[3+offset],
+        ["all in one fallback"] = searchers[4+offset], -- special case, combined libs
     },
     methods    = {
     },
@@ -84,28 +82,69 @@ package.helpers  = helpers
 local methods = helpers.methods
 local builtin = helpers.builtin
 
--- extra tds/ctx paths
+-- extra tds/ctx paths ... a bit of overhead for efficient tracing
 
 local extraluapaths = { }
 local extralibpaths = { }
 local luapaths      = nil -- delayed
 local libpaths      = nil -- delayed
+local oldluapath    = nil
+local oldlibpath    = nil
+
+local nofextralua   = -1
+local nofextralib   = -1
+local nofpathlua    = -1
+local nofpathlib    = -1
+
+local function listpaths(what,paths)
+    local nofpaths = #paths
+    if nofpaths > 0 then
+        for i=1,nofpaths do
+            helpers.report("using %s path %i: %s",what,i,paths[i])
+        end
+    else
+        helpers.report("no %s paths defined",what)
+    end
+    return nofpaths
+end
 
 local function getextraluapaths()
+    if helpers.trace and #extraluapaths ~= nofextralua then
+        nofextralua = listpaths("extra lua",extraluapaths)
+    end
     return extraluapaths
 end
 
 local function getextralibpaths()
+    if helpers.trace and #extralibpaths ~= nofextralib then
+        nofextralib = listpaths("extra lib",extralibpaths)
+    end
     return extralibpaths
 end
 
 local function getluapaths()
-    luapaths = luapaths or file.splitpath(package.path, ";")
+    local luapath = package.path or ""
+    if oldluapath ~= luapath then
+        luapaths   = file.splitpath(luapath,";")
+        oldluapath = luapath
+        nofpathlua = -1
+    end
+    if helpers.trace and #luapaths ~= nofpathlua then
+        nofpathlua = listpaths("builtin lua",luapaths)
+    end
     return luapaths
 end
 
 local function getlibpaths()
-    libpaths = libpaths or file.splitpath(package.cpath, ";")
+    local libpath = package.cpath or ""
+    if oldlibpath ~= libpath then
+        libpaths   = file.splitpath(libpath,";")
+        oldlibpath = libpath
+        nofpathlib = -1
+    end
+    if helpers.trace and #libpaths ~= nofpathlib then
+        nofpathlib = listpaths("builtin lib",libpaths)
+    end
     return libpaths
 end
 
@@ -167,8 +206,10 @@ end
 -- lib loader (used elsewhere)
 
 local function loadedaslib(resolved,rawname) -- todo: strip all before first -
- -- local init = "luaopen_" .. string.match(rawname,".-([^%.]+)$")
-    local init = "luaopen_"..gsub(rawname,"%.","_")
+    local base = gsub(rawname,"%.","_")
+ -- so, we can do a require("foo/bar") and initialize bar
+ -- local base = gsub(file.basename(rawname),"%.","_")
+    local init = "luaopen_" .. gsub(base,"%.","_")
     if helpers.trace then
         helpers.report("calling loadlib with '%s' with init '%s'",resolved,init)
     end
@@ -180,159 +221,120 @@ helpers.loadedaslib = loadedaslib
 -- wrapped and new loaders
 
 local function loadedbypath(name,rawname,paths,islib,what)
-    local trace  = helpers.trace
-    local report = helpers.report
-    if trace then
-        report("locating '%s' as '%s' on '%s' paths",rawname,name,what)
-    end
+    local trace = helpers.trace
     for p=1,#paths do
-        local path = paths[p]
+        local path     = paths[p]
         local resolved = filejoin(path,name)
-        if trace then -- mode detail
-            report("checking '%s' using '%s' path '%s'",name,what,path)
+        if trace then
+            helpers.report("%s path, identifying '%s' on '%s'",what,name,path)
         end
         if isreadable(resolved) then
             if trace then
-                report("'%s' located on '%s'",name,resolved)
+                helpers.report("%s path, '%s' found on '%s'",what,name,resolved)
             end
-            local result = nil
             if islib then
-                result = loadedaslib(resolved,rawname)
+                return loadedaslib(resolved,rawname)
             else
-                result = loadfile(resolved)
+                return loadfile(resolved)
             end
-            if result then
-                result()
-            end
-            return true, result
         end
     end
 end
 
 helpers.loadedbypath = loadedbypath
 
--- alternatively we could set the package.searchers
-
 methods["already loaded"] = function(name)
-    local result = package.loaded[name]
-    if result then
-        return true, result
-    end
+    return package.loaded[name]
 end
 
 methods["preload table"] = function(name)
-    local result = builtin["preload table"](name)
-    if type(result) == "function" then
-        return true, result
-    end
+    return builtin["preload table"](name)
 end
 
 methods["lua extra list"] = function(name)
-    local thename  = lualibfile(name)
-    local luaname  = addsuffix(thename,"lua")
-    local luapaths = getextraluapaths()
-    local done, result = loadedbypath(luaname,name,luapaths,false,"lua")
-    if done then
-        return true, result
-    end
+    return loadedbypath(addsuffix(lualibfile(name),"lua"       ),name,getextraluapaths(),false,"lua")
 end
 
 methods["lib extra list"] = function(name)
-    local thename  = lualibfile(name)
-    local libname  = addsuffix(thename,os.libsuffix)
-    local libpaths = getextralibpaths()
-    local done, result = loadedbypath(libname,name,libpaths,true,"lib")
-    if done then
-        return true, result
-    end
+    return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true, "lib")
 end
 
-local shown = false
-
 methods["path specification"] = function(name)
-    if not shown and helpers.trace then
-        local luapaths = getluapaths() -- triggers list building
-        if #luapaths > 0 then
-            helpers.report("using %s built in lua paths",#luapaths)
-        else
-            helpers.report("no built in lua paths defined")
-        end
-        shown = true
-    end
-    local result = builtin["path specification"](name)
-    if type(result) == "function" then
-        return true, result()
-    end
+    getluapaths() -- triggers list building and tracing
+    return builtin["path specification"](name)
 end
 
-local shown = false
-
 methods["cpath specification"] = function(name)
-    if not shown and helpers.trace then
-        local libpaths = getlibpaths() -- triggers list building
-        if #libpaths > 0 then
-            helpers.report("using %s built in lib paths",#libpaths)
-        else
-            helpers.report("no built in lib paths defined")
-        end
-        shown = true
-    end
-    local result = builtin["cpath specification"](name)
-    if type(result) == "function" then
-        return true, result()
-    end
+    getlibpaths() -- triggers list building and tracing
+    return builtin["cpath specification"](name)
 end
 
 methods["all in one fallback"] = function(name)
-    local result = builtin["all in one fallback"](name)
-    if type(result) == "function" then
-        return true, result()
-    end
+    return builtin["all in one fallback"](name)
 end
 
 methods["not loaded"] = function(name)
     if helpers.trace then
-        helpers.report("unable to locate '%s'",name)
+        helpers.report("unable to locate '%s'",name or "?")
     end
+    return nil
 end
 
+local level = 0
+local used  = { }
+
+helpers.traceused = false
+
 function helpers.loaded(name)
     local sequence = helpers.sequence
+    level = level + 1
     for i=1,#sequence do
-        local step = sequence[i]
+        local method = sequence[i]
         if helpers.trace then
-            helpers.report("locating '%s' using method '%s'",name,step)
+            helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name)
         end
-        local done, result = methods[step](name)
-        if done then
+        local result, rest = methods[method](name)
+        if type(result) == "function" then
             if helpers.trace then
-                helpers.report("'%s' located via method '%s' returns '%s'",name,step,type(result))
+                helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name)
             end
-            if result then
-                package.loaded[name] = result
+            if helpers.traceused then
+                used[#used+1] = { level = level, name = name }
             end
-            return result
+            level = level - 1
+            return result, rest
         end
     end
-    return nil -- we must return a value
+    -- safeguard, we never come here
+    level = level - 1
+    return nil
+end
+
+function helpers.showused()
+    local n = #used
+    if n > 0 then
+        helpers.report("%s libraries loaded:",n)
+        helpers.report()
+        for i=1,n do
+            local u = used[i]
+            helpers.report("%i %a",u.level,u.name)
+        end
+        helpers.report()
+     end
 end
 
 function helpers.unload(name)
     if helpers.trace then
         if package.loaded[name] then
-            helpers.report("unloading '%s', %s",name,"done")
+            helpers.report("unloading, name '%s', %s",name,"done")
         else
-            helpers.report("unloading '%s', %s",name,"not loaded")
+            helpers.report("unloading, name '%s', %s",name,"not loaded")
         end
     end
-    package.loaded[name] = nil -- does that work? is readable only, maybe we need our own hash
+    package.loaded[name] = nil
 end
 
-searchers[1] = nil
-searchers[2] = nil
-searchers[3] = nil
-searchers[4] = nil
-
-helpers.savedrequire = helpers.savedrequire or require
+-- overloading require does not work out well so we need to push it in
+-- front ..
 
-require = helpers.loaded
+table.insert(searchers,1,helpers.loaded)
diff --git a/lualibs-table.lua b/lualibs-table.lua
index 640bbbb..e57abe8 100644
--- a/lualibs-table.lua
+++ b/lualibs-table.lua
@@ -1094,7 +1094,7 @@ function table.tofile(filename,root,name,specification)
     end
 end
 
-local function flattened(t,f,depth)
+local function flattened(t,f,depth) -- also handles { nil, 1, nil, 2 }
     if f == nil then
         f = { }
         depth = 0xFFFF
@@ -1110,19 +1110,16 @@ local function flattened(t,f,depth)
             if depth > 0 and type(v) == "table" then
                 flattened(v,f,depth-1)
             else
-                f[k] = v
+                f[#f+1] = v
             end
         end
     end
-    local n = #f
     for k=1,#t do
         local v = t[k]
         if depth > 0 and type(v) == "table" then
             flattened(v,f,depth-1)
-            n = #f
         else
-            n = n + 1
-            f[n] = v
+            f[#f+1] = v
         end
     end
     return f
diff --git a/lualibs-util-env.lua b/lualibs-util-env.lua
index 283b91c..f4f3ef6 100644
--- a/lualibs-util-env.lua
+++ b/lualibs-util-env.lua
@@ -206,6 +206,35 @@ function environment.reconstructcommandline(arg,noquote)
     end
 end
 
+-- handy in e.g. package.addluapath(environment.relativepath("scripts"))
+
+function environment.relativepath(path,root)
+    if not path then
+        path = ""
+    end
+    if not file.is_rootbased_path(path) then
+        if not root then
+            root = file.pathpart(environment.ownscript or environment.ownname or ".")
+        end
+        if root == "" then
+            root = "."
+        end
+        path = root .. "/" .. path
+    end
+    return file.collapsepath(path,true)
+end
+
+-- -- when script lives on e:/tmp we get this:
+--
+-- print(environment.relativepath("x/y/z","c:/w")) -- c:/w/x/y/z
+-- print(environment.relativepath("x"))            -- e:/tmp/x
+-- print(environment.relativepath("../x"))         -- e:/x
+-- print(environment.relativepath("./x"))          -- e:/tmp/x
+-- print(environment.relativepath("/x"))           -- /x
+-- print(environment.relativepath("c:/x"))         -- c:/x
+-- print(environment.relativepath("//x"))          -- //x
+-- print(environment.relativepath())               -- e:/tmp
+
 -- -- to be tested:
 --
 -- function environment.reconstructcommandline(arg,noquote)
-- 
cgit v1.2.3