Warning: this is an htmlized version!
The original is across this link,
and the conversion rules are here.
-- -*- coding: raw-text-unix; backup-by-copying: t -*-
-- This file: http://angg.twu.net/LUA/lua50init.lua.html
--            http://angg.twu.net/dednat4/edrxlib.lua.html
--            http://angg.twu.net/blogme3/edrxlib.lua.html
-- On my system ~/LUA/lua50init.lua the two "edrxlib.lua"s
-- are hard linked:
--   (find-sh0 "cp -flv ~/LUA/lua50init.lua ~/dednat4/edrxlib.lua")
--   (find-sh0 "cp -flv ~/LUA/lua50init.lua ~/blogme3/edrxlib.lua")
--   (find-sh0 "ls -li  ~/LUA/lua50init.lua ~/{dednat4,blogme3}/edrxlib.lua")
--   (find-tkdiff "~/LUA/lua50init.lua" "~/dednat4/edrxlib.lua")
--   (find-tkdiff "~/LUA/lua50init.lua" "~/dednat4/blogme3.lua")
--   (find-elnode "Rename or Copy" "hard links")
--   (find-elnode "Rename or Copy" "backup-by-copying")
--
-- This is my "init file" for Lua. As I have LUA_INIT set
-- to "@$HOME/LUA/lua50init.lua", the Lua interpreter loads
-- this on start-up. 
-- See: (find-angg ".zshrc" "lua" "LUA_INIT")
--      (find-luamanualw3m "#6" "LUA_INIT" "@filename")
--      (find-man "1 lua50" "LUA_INIT")
--      (find-man "1 lua51" "LUA_INIT")
--      (find-lua51w3m "doc/lua.html" "LUA_INIT")
--      http://www.lua.org/manual/5.1/lua.html
--      http://linux.die.net/man/1/lua
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2013sep14   <- don't trust this date
-- Public domain.
--
-- Note: "dednat4.lua" tries to load this at startup, with
-- 'require "edrxlib"', just after setting the path; if this has
-- already been loaded by LUA_INIT, then the 'require "edrxlib"'
-- is a no-op, because of the "package.loaded.edrxlib = ..." at
-- the end of this file - otherwise it is loaded, probably from
-- the "~/dednat4/" dir. See this for the details:
--      (find-dn4 "dednat4.lua" "edrxlib")
--
-- Blogme3 does the same trick:
--      (find-blogme3 "blogme3.lua" "edrxlib")
--
-- This file used to work both on lua-5.0 and lua-5.1 -
-- but now I have stopped using lua-5.0, and I'm trying
-- (slowly!) to make this more readable, remove the
-- cruft, some 5.0-isms, some obsolete loaders, etc.


-- «.escripts»		(to "escripts")
-- «.build-lua»		(to "build-lua")
-- «.compat»		(to "compat")
-- «.string-methods»	(to "string-methods")
-- «.otherfunctions»	(to "otherfunctions")
-- «.printf»		(to "printf")
-- «.pack-and-unpack»	(to "pack-and-unpack")
-- «.0-based»		(to "0-based")
-- «.P»			(to "P")
-- «.PP»		(to "PP")
-- «.PPP»		(to "PPP")
-- «.tests»		(to "tests")
-- «.envsubst»		(to "envsubst")
-- «.readfile»		(to "readfile")
-- «.writefile»		(to "writefile")
-- «.mytostring»	(to "mytostring")
-- «.mysortedpairs»	(to "mysortedpairs")
-- «.mytostringk2»	(to "mytostringk2")
-- «.tos»		(to "tos")
-- «.split»		(to "split")
-- «.ee_expand»		(to "ee_expand")
-- «.ee_dofile»		(to "ee_dofile")
-- «.ee_loadlib»	(to "ee_loadlib")
-- «.untabify»		(to "untabify")
-- «.load_dednat4»	(to "load_dednat4")
-- «.load_rex»		(to "load_rex")
-- «.load_posix»	(to "load_posix")
-- «.load_PP»		(to "load_PP")
-- «.PPeval»		(to "PPeval")
-- «.loadswigso»	(to "loadswigso")
-- «.loadcinvoke»	(to "loadcinvoke")
-- «.loadlpeg»		(to "loadlpeg")
-- «.loadbitlib»	(to "loadbitlib")
-- «.autoload»		(to "autoload")
-- «.loadtcl»		(to "loadtcl")
-- «.loadldb»		(to "loadldb")
-- «.loadpeek»		(to "loadpeek")
-- «.loadalarm»		(to "loadalarm")
-- «.loadposix»		(to "loadposix")
-- «.getoutput»		(to "getoutput")
-- «.preparef2n»	(to "preparef2n")
-- «.map»		(to "map")
-- «.fold»		(to "fold")
-- «.gformat»		(to "gformat")
-- «.each2»		(to "each2")
-- «.splitlines»	(to "splitlines")
-- «.translatechars»	(to "translatechars")
-- «.sbeconcat»		(to "sbeconcat")
-- «.concatbestrings»	(to "concatbestrings")
-- «.lpeg_togsub»	(to "lpeg_togsub")
-- «.lpeg_gsub»		(to "lpeg_gsub")
-- «.lpeg_gsub_»	(to "lpeg_gsub_")
-- «.lpeg_balanced»	(to "lpeg_balanced")
-- «.mytraceback»	(to "mytraceback")
-- «.errorfb_line»	(to "errorfb_line")
-- «.ee_template»	(to "ee_template")
-- «.ee_into»		(to "ee_into")
-- «.chdir»		(to "chdir")
-- «.package.require»	(to "package.require")
-- «.userocks»		(to "userocks")
-- «.loadblogme3»	(to "loadblogme3")
-- «.icollect»		(to "icollect")
-- «.over0»		(to "over0")
-- «.interactor»	(to "interactor")
-- «.eoo»		(to "eoo")
-- «.over»		(to "over")
-- «.follow»		(to "follow")
-- «.NamedFunction»	(to "NamedFunction")
-- «.Repl»		(to "Repl")
-- «.replaceranges»	(to "replaceranges")
-- «.Sexp»		(to "Sexp")
-- «.youtube_split»	(to "youtube_split")


-- «escripts»  (to ".escripts")
-- «build-lua»  (to ".build-lua")
-- (find-es "lua5" "install-5.1.2")

-- «compat»  (to ".compat")
-- On Lua 4.x these functions had the short names on the left; 
-- on Lua-5.0.x a file etc/compat.lua could be used to make the short
-- names work, but on Lua-5.1.x this compat.lua has been dropped...
-- I still like the short names, so:

-- (find-lua51file "src/lstrlib.c" "{\"find\", str_find},")
-- (find-lua50file "etc/compat.lua" "strfind = str.find")
write    = io.write        -- (find-luamanualw3m "#pdf-io.write")
format   = string.format   -- (find-luamanualw3m "#pdf-string.format")
gsub     = string.gsub     -- (find-luamanualw3m "#pdf-string.gsub")
strfind  = string.find     -- (find-luamanualw3m "#pdf-string.find")
strlen   = string.len      -- (find-luamanualw3m "#pdf-string.len")
strsub   = string.sub      -- (find-luamanualw3m "#pdf-string.sub")
concat   = table.concat    -- (find-luamanualw3m "#pdf-table.concat")
tinsert  = table.insert    -- (find-luamanualw3m "#pdf-table.insert")
tremove  = table.remove    -- (find-luamanualw3m "#pdf-table.remove")

-- foreachi = table.foreachi -- (find-luamanualw3m "#7.2" "table.foreachi")
getn     = table.getn        -- (find-luamanualw3m "#7.2" "table.getn")



-- «string-methods»  (to ".string-methods")
-- A note about "string methods": if s is a string, then a piece of
-- code like "s:rep(2)" works like "string.rep(s, 2)"; this is a
-- Lua-5.1-ism that is not described in the first edition of PiL - the
-- one that is online, that covers only Lua 5.0. When we do
--
--   s = "foo"
--   print(s:rep(2))
--
-- then the "s:rep(2)" is syntax sugar for 's["rep"](s,2)'. At first
-- sight, the table access s["rep"] should fail, but in 5.1 strings
-- have a metatable like this:
--
--   setmetatable("str", {__index = string})
--
-- and so instead of failing Lua does something else... the s["rep"]
-- becomes getmetatable(s).__index["rep"], and that is just
-- string["rep"], i.e., string.rep; so, s:rep(2) works like
-- string.rep(s, 2).
--
-- See:
-- (find-luamanualw3m "#2.2"   "a.name as syntactic sugar")
-- (find-luamanualw3m "#2.5.8" "v:name(args)" "v.name(v,args)")
-- (find-luamanualw3m "#2.8" "Tables and userdata have individual metatables")
-- (find-luamanualw3m "#2.8" "table[key]" "h = metatable(table).__index")
-- (find-luamanualw3m "#5.4" "object-oriented style" "s:byte(i)")
-- (find-luamanualw3m "#pdf-string.rep")
-- (find-pilw3m "13.4.1.html" "The __index Metamethod")



-- «otherfunctions»  (to ".otherfunctions")

-- «printf»  (to ".printf")
-- printf = function (...) write(format(unpack(arg))) end
printf = function (...) write(format(...)) end


-- «pack-and-unpack»  (to ".pack-and-unpack")
-- (find-es "lua5" "pack-and-unpack")
-- (find-es "lua5" "LUA_COMPAT_VARARG")
-- (find-luamanualw3m "#pdf-unpack")
-- (find-luamanualw3m "#7.1" "pseudo-argument arg")
-- (find-lua51manualw3m "#pdf-table.unpack")
-- (find-lua52manualw3m "#pdf-table.unpack")
-- (find-lua52manualw3m "#pdf-table.pack")
-- (find-lua51manualw3m "#pdf-select")
-- (find-lua52manualw3m "#pdf-select")
-- 5.1 only:
-- pack     = function (...) return arg end
-- myunpack = function (arg) return unpack(arg, 1, arg.n) end

-- These definitions should work both on 5.1 and on 5.2:
pack     = table.pack or function (...) return arg end   -- 5.1 and 5.2
pack     = table.pack or function (...) return {n=select("#", ...), ...} end
unpack   = unpack or table.unpack
myunpack = function (arg) return unpack(arg, 1, arg.n) end

-- New:
-- pack   = table.pack or function (...) return {n=select("#", ...), ...} end
-- unpack = function (T) return table.unpack(T, 1, T.n)

-- Examples:
--      PP(pack(nil, 22, nil, 44, nil))        -->  {2=22, 4=44, "n"=5}
--   PP(unpack({nil, 22, nil, 44, nil, n=5}))  -->  <nil> 22
-- PP(myunpack({nil, 22, nil, 44, nil, n=5}))  -->  <nil> 22 <nil> 44 <nil>



-- «0-based»  (to ".0-based")
-- (find-es "lua5" "0-based")
-- 0-based string functions.
-- (To do: remove this! I think I only use 0-based string functions at
-- dednat4 - and now I'm almost getting used to the 1-based
-- conventions...)
-- (find-sh "lua -e \"print(substr0('abcdef', 2, 3)) --> cde\"")
substr0 = function (str, start0, len)
    return string.sub(str, start0 + 1, len and start0 + len)
  end



-- «P»  (to ".P")
-- Like "print", but distinguishing strings from numbers, and using "<>"s.
-- See: (find-luamanualw3m "#pdf-type")
-- Examples:
--  print(nil, 22, "33", {}, false, print)
-->  nil   22   33   table: 0x806da60   false   function: 0x806b388
--  P(nil, 22, "33", {}, false, print)
-->  <nil> 22 "33" <table> <boolean> <function>
--
P = function (...)
    local arg = arg or pack(...)   -- for Lua 5.2
    for i=1,arg.n do
      local v = arg[i]
      if     type(v)=="number" then printf(" %d", v)
      elseif type(v)=="string" then printf(" %q", v)
      else printf(" <%s>", type(v))
      end
    end
    print()
  end

-- Note: "table.foreach(t, print)" is often enough for inspecting tables.
-- Ref: http://lua-users.org/lists/lua-l/2008-02/msg00932.html
--      http://lua-users.org/lists/lua-l/2008-02/msg00944.html


-- «PP»  (to ".PP")
-- (to "mytostring")
-- My favourite function for inspecting data!
-- This is like "print" too, but it uses "mytostring" to print the
-- contents of tables recursively. The output format is compact,
-- human-friendly, and simple to understand and to implement. Note: on
-- cyclic structures "mytostring" will loop and break; and metatables
-- are ignored (I use them very rarely, btw).
-- Examples:
--  PP(nil, true, false, 22, "22", "a\nb", print, nil)
-->   <nil> <true> <false> 22 "22" "a\
--    b" <function: 0x806b388> <nil>
--
--  PP({44, 55, nil, 77, [{a=11}]={[22]="b"}, [{}]={}, [{}]={}})
-->    {1=44, 2=55, 4=77, {"a"=11}={22="b"}, {}={}, {}={}}
--
PP = function (...)
    local arg = arg or pack(...)   -- for Lua 5.2
    for i=1,arg.n do printf(" %s", mytostring(arg[i])) end
    printf("\n")
    return myunpack(arg)    -- todo: change to "..." (a 5.1-ism)
  end

-- «PPP»  (to ".PPP")
-- Useful for debugging sometimes.
-- I don't use this much.
-- PP(string.rep("ab", 4))
-->              "abababab"
-- PP(string.rep(PPP("rep:")("ab", 4)))
-->                   (rep: "ab" 4)"abababab"
PPP = function (idstr)
    return function (...)
        printf("(%s", idstr)
        for i=1,arg.n do printf(" %s", mytostring(arg[i])) end
        printf(")")
        return unpack(arg)
      end
  end

-- «tests»  (to ".tests")
-- P(string.find("0123456789", "3(45)(67)", 4))  --> 4 8 "45" "67"
-- P(string.find("0123456789", "3(45)(67)", 5))  --> <nil>



-- «envsubst»  (to ".envsubst")
-- (find-es "lua5" "envsubst")
setenv_ = {}
setenv = function (varname, value) setenv_[varname] = value end
getenv = function (varname) return setenv_[varname] or os.getenv(varname) end
envsubst = function (str)
     return string.gsub(str, "%$([%a_][%w_]*)", function (e)
         return getenv(e) or ""
       end)
   end

-- «readfile»  (to ".readfile")
-- «writefile»  (to ".writefile")
-- (find-es "lua5" "readfile")
-- (find-luamanualw3m "#pdf-io.open")
-- (find-luamanualw3m "#pdf-file:read")
-- (find-luamanualw3m "#pdf-file:write")
readfile = function (fname)
    local f = assert(io.open(fname, "r"))
    local bigstr = f:read("*a")
    f:close()
    return bigstr
  end
writefile = function (fname, bigstr)
    local f = assert(io.open(fname, "w+"))
    f:write(bigstr)
    f:close()
  end

-- (find-blogme3file "youtube.lua" "ee_readfile =")
ee_readfile = function (fname) return readfile(ee_expand(fname)) end

-- «mytostring»  (to ".mytostring")
-- «tos»  (to ".tos")
-- 2011apr10: Rewrote all this in a hurry.
-- These functions are used by: (to "PP")
-- (find-es "lua5" "mytostring")
-- Possible replacements:
--   (find-angg "LUA/tos.lua")
--   (find-angg "LUA/tos2.lua")
--   (find-dn5 "tos.lua")
tos_compare_pairs = function (pair1, pair2)
    local key1,  key2  = pair1.key,  pair2.key
    local type1, type2 = type(key1), type(key2)
    if type1 == type2 then
      if type1 == "number" then return key1 < key2 end
      if type1 == "string" then return key1 < key2 end
      return tostring(key1) < tostring(key2)  -- fast
    else
      return type1 < type2   -- numbers before strings before tables, etc
    end
  end
tos_sorted_pairs = function (T)
    local Tpairs = {}
    for key,val in pairs(T) do
      table.insert(Tpairs, {key=key, val=val})
    end
    return sorted(Tpairs, tos_compare_pairs)
  end
tos_table_orig = function (T, sep)
    return "{"..mapconcat(tos_pair, tos_sorted_pairs(T), sep or ", ").."}"
  end
tos_table = tos_table_orig
tos = function (o)
    local t = type(o)
    if t=="number" then return tostring(o) end
    if t=="string" then return format("%q", o) end
    if t=="table"  then return tos_table(o) end
    return "<"..tostring(o)..">"
  end
tos_key = tos              -- change this to print string keys differently
tos_pair = function (pair)
    return tos_key(pair.key).."="..tos(pair.val)
  end

mysort = tos_sorted_pairs   -- compatibility
mytostring = tos            -- compatibility
mytostring_arg = function (T, sep)
    return mapconcat(tos, T, sep or " ", T.n)
  end

-- Tools for building extensions
tos_good_string_key = function (key)
    return type(key) == "string" and key:match("^[A-Za-z_][A-Za-z_0-9]*$")
  end
tos_has_tostring = function (o)
    return getmetatable(T) and getmetatable(T).__tostring
  end
tos_has_eootype = function (o)
    return type(o) == "table" and getmetatable(o) and getmetatable(o).type
  end



-- mysort = function (T)
--     local Tpairs = {}
--     for key,val in pairs(T) do
--       table.insert(Tpairs, {key=key, val=val})
--     end
--     return sorted(Tpairs, mysort_lt)
--   end

-- mytostring_table_orig = function (T, sep)
--     return "{"..mapconcat(mytostring_pair, mysort(T), sep or ", ").."}"
--   end
-- mytostring_table_new = function (T, sep)
--     if getmetatable(T) and getmetatable(T).__tostring then return tostring(T) end
--     return mytostring_table_orig(T, sep)
--   end
-- mytostring_table = mytostring_table_new
-- 
-- mytostring = function (o)
--     local t = type(o)
--     if t=="number" then return tostring(o) end
--     if t=="string" then return format("%q", o) end
--     if t=="table"  then return mytostring_table(o) end
--     return "<"..tostring(o)..">"
--   end


-- mytostringk = mytostring   -- change this to print string keys differently
-- 
-- mytostring_arg = function (arg, sep)
--     local images = {}
--     for i=1,arg.n do images[i] = mytostring(arg[i]) end
--     return table.concat(images, sep or " ")
--   end

-- mytostring_arg({n=4, nil, 22, 33, nil})
-->                   "<nil> 22 33 <nil>"

-- «mysortedpairs»  (to ".mysortedpairs")
-- This is useful in iteractive scripts. The name is bad, I know.
-- (find-pilw3m "7.1.html" "simple iterator")
mysortedpairs = function (T)
    local T = mysort(T)
    local i,n = 0,#T
    return function ()
        i = i + 1
        if i <= n then return T[i].key,T[i].val end
      end
  end

-- «mytostringk2»  (to ".mytostringk2")
-- Experimental. Usage:
--   mytostringk = mytostringk2
mytostringk2 = function (o)
    if type(o) == "string" and o:match("^[A-Za-z_][A-Za-z_0-9]*$") then
      return o
    else
      return mytostring(o)
    end
  end



-- «split»  (to ".split")
-- (find-es "lua5" "split")
split = function (str, pat)
    local arr = {}
    string.gsub(str, pat or "([^%s]+)", function (word)
        table.insert(arr, word)
      end)
    return arr
  end

-- «ee_expand»  (to ".ee_expand")
-- (find-eev "eev.el" "ee-expand")
ee_expand = function (path)
    path = string.gsub(path, "^~$", "$HOME/", 1)
    path = string.gsub(path, "^~/", "$HOME/", 1)
    path = string.gsub(path, "^%$(%w+)", os.getenv, 1)
    return path
  end

-- «ee_dofile»  (to ".ee_dofile")
-- «ee_loadlib»  (to ".ee_loadlib")
ee_dofile  = function (path) return dofile(ee_expand(path)) end
ee_loadlib = function (libname, funcname)
    return assert(package.loadlib(ee_expand(libname), funcname))()
  end


-- «untabify»  (to ".untabify")
-- Note: to untabify strings in encodings where chars can be more than
-- 1-byte long, change the "strlen" below... (I never had to do that,
-- though).
untabify_table =
  {"        ", "       ", "      ", "     ", "    ", "   ", "  ", " "}
--{"--------", "-------", "------", "-----", "----", "---", "--", "-"}
untabify_strtab = function (strbeforetab)
    return strbeforetab ..
      untabify_table[math.mod(strlen(strbeforetab), 8) + 1]
  end
untabify = function (str)
    return (gsub(str, "([^\t\r\n]*)\t", untabify_strtab))
  end


-- (find-luamanualw3m "#pdf-math.min")
-- (find-luamanualw3m "#pdf-math.max")
-- PP(math.min("22", "200"))  --> 22
--      PP(min("22", "200"))  --> "200"
min = function (a, b)
    if a < b then return a else return b end
  end
max = function (a, b)
    if a < b then return b else return a end
  end




-- «load_dednat4»  (to ".load_dednat4")
-- (find-angg ".emacs" "eepitch-dednat4")
-- (find-es    "xypic" "eepitch-dednat4")
-- (find-dn4 "dednat4.lua" "diag-head")
-- (find-dn4 "dednat4.lua" "abbrev-head")
-- (find-dn4 "dednat4.lua" "tree-head" "treeheadcode1")
-- (find-dn4 "dednat4.lua" "processfile")
-- (defun eepitch-dednat4 () (interactive) (eepitch-comint "dednat4" "lua51 -e load_dednat4() -i"))
-- (eepitch-kill)
-- (eepitch-dednat4)
load_dednat4 = function ()
    dednat4dir = dednat4dir or ee_expand("~/dednat4/")
    print("Loading: " .. dednat4dir .. "dednat4.lua")
    dofile(dednat4dir .. "dednat4.lua")
    A  = function (abbrev, expansion) addabbrev(abbrev, expansion) end
    D  = function (linestr) dofs(untabify(linestr)) end
    DX = function (linestr) dxy2Dx(untabify(linestr)) end
    D2 = function (linestr) dxy2D(untabify(linestr)) end
  end


-- «load_rex»  (to ".load_rex")
-- (find-es "lua5" "rexlib")
-- Usage: if not rex then load_rex() end
-- Note (2007): I haven't used this in ages!
-- Lpeg is much better, and nowadays I would try to use "require" here
-- instead of loadlib...
load_rex = function ()
    assert(loadlib(getenv("HOME").."/.lua50/lrexlib.so", "luaopen_rex"))()
    setmetatable(rex,
      {__call = function (self, p, cf, lo) return self.newPOSIX(p, cf, lo) end})
    function rex.find(s, p, st)   return rex(p):match(s, st) end
    function rex.gsub(s, p, f, n) return rex(p):gmatch(s, f, n) end
  end

-- «load_posix»  (to ".load_posix")
-- This is for lua-5.0, for 5.1 see: (to "loadposix")
-- (find-es "lua5" "load_posix")
-- (find-es "lua5" "posix-install")
load_posix = function ()
    assert(loadlib(getenv("HOME").."/.lua50/lposix.so", "luaopen_posix"))()
  end

-- «load_PP»  (to ".load_PP")
-- Load PP.so, that defines a C function called PP for inspecting the stack.
-- Old version, for lua-5.0:
-- -- (find-angg ".lua50/PP.c")
--    load_PP = function ()
--        assert(loadlib(getenv("HOME").."/.lua50/PP.so", "PP_init"))()
--      end
-- New version, for lua-5.1:
load_PP = function ()
    assert(package.loadlib(getenv("HOME").."/.lua51/PP.so", "PP_init"))()
  end
-- 2008dec01: load_PP is not needed for debugging anymore!...
-- The user-defined GDB command `PP' used to call the C function `PP',
-- that was defined in PP.c/PP.so - but I changed the GDB `PP' to make
-- it run directly all the calls to Lua that the C `PP' used to make.
-- See: (find-angg ".lua51/PP.c")
--      (find-angg ".lua51/PP.gdb")

-- «PPeval»  (to ".PPeval")
-- (find-angg ".lua51/PP.gdb" "PPeval")
-- (find-lua51file "src/lua.c" "first line starts with `=' ?")
PPeval = function (str)
    local e, code = string.match(str, "^(=?=?)(.*)$")
    local eval = function (str) return assert(loadstring(str))() end
    if     e == "==" then    PP(eval("return "..code))
    elseif e == "="  then print(eval("return "..code))
    else   return eval(code)
    end
  end

-- «loadswigso»  (to ".loadswigso")
-- (find-es "swig" "myswiglua")
-- Example: loadswigso("C", "./myparser.so", "parser", "countwords")
loadswigso = function (modulename, fname_so, ...)
    assert(loadlib(fname_so, modulename.."_Init"))()
    local module = _G[modulename]
    for i=1,arg.n do
      _G[arg[i]] = module[arg[i]]  -- export to the table of globals
    end
  end

-- «loadcinvoke»  (to ".loadcinvoke")
-- (find-es "lua5" "cinvoke")
loadcinvoke = function ()
    local oldcpath = package.cpath
    package.cpath = ee_expand("~/usrc/cinvoke-1.0/bindings/lua/?.so")
    require "cinvoke_lua"
    package.cpath = oldcpath
  end

-- «loadlpeg»  (to ".loadlpeg")
-- (find-es "lua5" "lpeg-0.7")
-- (find-es "lua5" "lpeg-0.8.1")
-- (find-es "lua5" "lpeg-0.9")
-- (find-es "lua5" "lpeg")
loadlpeg = function ()
    local oldcpath = package.cpath
    -- package.cpath = ee_expand("~/usrc/lpeg-0.4/?.so")
    -- package.cpath = ee_expand("~/usrc/lpeg-0.5/?.so")
    -- package.cpath = ee_expand("~/usrc/lpeg-0.7/?.so")..";"..oldcpath
    -- (find-luamanualw3m "#pdf-package.cpath")
    -- (find-sh0 "lua51 -e 'print(package.path)'")
    -- (find-sh0 "lua51 -e 'print(package.cpath)'")
    -- package.cpath = ee_expand("~/usrc/lpeg-0.8.1/?.so")..";"..oldcpath
    package.cpath = ee_expand("~/usrc/lpeg-0.9/?.so")..";"..oldcpath
    require "lpeg"
    package.cpath = oldcpath
    lpeg.test  = function (pat, str) PP(pat:C():match(str)) end
    lpeg.testt = function (pat, str) PP(pat:Ct():match(str)) end
    lpeg.togsub   = lpeg_togsub     -- (to "lpeg_togsub")
    lpeg.gsub     = lpeg_gsub       -- (to "lpeg_gsub")
    lpeg.gsub_    = lpeg_gsub_      -- (to "lpeg_gsub_")
    lpeg.Balanced = lpeg_balanced   -- (to "lpeg_balanced")
  end

-- «loadbitlib»  (to ".loadbitlib")
-- (find-es "lua5" "bitlib-51")
loadbitlib = function (fname)
    if bit then return "bitlib already loaded" end
    fname = fname or "~/usrc/bitlib-25/lbitlib.so"
    assert(package.loadlib(ee_expand(fname), "luaopen_bit"))()
  end

-- «autoload»  (to ".autoload")
-- Like in elisp. For global functions only.
-- (find-luamanualw3m "#pdf-require")
--
autoload = function (funname, loader)
    _G[funname] = function (...)
        loader()
        return _G[funname](unpack(arg)) -- todo: change to "..." (a 5.1-ism)
      end
  end

tcl = function (...)   -- <-- this is a kind of autoload
    local filename = ee_expand("~/.lua51/luatclbridge.so")
    local initname = "luaopen_luatclbridge"
    tcl = assert(package.loadlib(filename, initname))()
    return tcl(unpack(arg))             -- todo: change to "..." (a 5.1-ism)
  end

-- «loadtcl»  (to ".loadtcl")
-- (find-es "lua5" "luatclbridge")
-- (find-angg "LUA/luatclbridge.c")
-- loadtcl = function ()
--     local filename = ee_expand("~/LUA/tlbridge.so")
--     local initname = "luaopen_tlbridge"
--     tcl = tcl or assert(package.loadlib(filename, initname))()
--   end
loadtcl = function ()
    -- local filename = ee_expand("~/LUA/luatclbridge.so")
    local filename = ee_expand("~/.lua51/luatclbridge.so")
    local initname = "luaopen_luatclbridge"
    if not tcl then
      tcl, tclfindexecutable = assert(package.loadlib(filename, initname))()
      tclfindexecutable("/home/edrx/usrc/tk8.4/tk8.4-8.4.12/unix/wish") -- test
    end
  end
loadtk     = function () loadtcl(); return tcl("package require Tk") end
loadexpect = function () loadtcl(); return tcl("package require Expect") end
loadsnack  = function () loadtcl(); return tcl("package require sound") end
-- (find-es "tcl" "snack")
-- (find-anggfile "TCL/piano.tcl")

-- «loadldb»  (to ".loadldb")
-- (find-es "lua5" "ldb-from-tgz")
-- (find-es "lua5" "ldb")
loadldb = function ()
    local oldpath = package.path
    -- package.path = ee_expand("$S/http/primero.ricilake.net/lua/?.lua")
    -- package.path = ee_expand("~/LUA/?.lua")
    package.path = ee_expand("~/usrc/ldb/?.lua")
    ldb = require "ldb"
    package.path = oldpath
  end

-- «loadpeek»  (to ".loadpeek")
-- (find-angg "DAVINCI/peek.c")
-- (find-angg "DAVINCI/peek.lua")
loadpeek = function ()
    if not peek then
      assert(package.loadlib(ee_expand("~/DAVINCI/peek.so"), "peek_init"))()
    end
  end
getaddr = function (obj)
    return tonumber(string.match(tostring(obj), " 0x([0-9A-Za-z]+)"), 16)
  end

-- «loadalarm»  (to ".loadalarm")
-- (find-es "lua5" "signal")
loadalarm = function ()
    if not alarm then
      assert(package.loadlib(ee_expand("~/usrc/alarm/lalarm.so"), "luaopen_alarm"))()
    end
  end

-- «loadposix»  (to ".loadposix")
-- New way (active below):  (find-es "lua5" "luaposix")
-- old way (commented out): (find-es "lua5" "posix-lua51")
loadposix = function ()
    if not posix then
      -- assert(package.loadlib(ee_expand("~/usrc/posix/lposix.so"), "luaopen_posix"))()
      ee_loadlib("~/usrc/luaposix-5.1.4/posix.so", "luaopen_posix")
    end
  end

-- «getoutput»  (to ".getoutput")
-- (find-es "lua5" "getoutput")
getoutput = function (command)
    local pipe = assert(io.popen(command))
    local output = pipe:read("*a")
    pipe:close()
    return output
  end


-- «preparef2n»  (to ".preparef2n")
-- (find-es "lua5" "functionnames")
-- preparef2n: create a table with names of functions.
-- Example:
--   f2n = preparef2n()
--   print(f2n(loadstring)) --> "loadstring"
--
-- This is new (2007mar11), and not very well-tested.
-- Note: there's no support yet for submodules (like "socket.http").
-- I wrote this for my traceback functions...
--
preparef2n__ = function (fun2name, dictname, dictnamedot, dict)
    for name,value in pairs(dict or _G) do
      if type(value) == "function" then
        if string.match(name, "^[A-Za-z_][0-9A-Za-z_]*$") then
          fun2name[value] = dictnamedot..name
        else
          fun2name[value] = string.format("%s[%q]", dictname, name)
        end
      end
    end
  end

preparef2n_ = function (fun2name, dictnames)
    for _,dn in ipairs(split(dictnames)) do
      if dn == "_G"
      then preparef2n__(fun2name, "_G", "",      _G)
      else preparef2n__(fun2name, dn,   dn..".", _G[dn])
      end
    end
  end

preparef2n = function (otherdictnames)
    local f2n = {}
    local standarddicts = " coroutine debug io math os package string table "
    preparef2n_(f2n, standarddicts .. (otherdictnames or "") .. " _G ")
    return f2n
  end


-- «map»  (to ".map")
--------[ keys, map, seq, nop, each2, splitlines, chartranslator ]--------

keys = function (tbl)
    local ks = {}
    for k,_ in pairs(tbl) do tinsert(ks,k) end
    return ks
  end

map = function (f, arr, n)
    local brr = {}
    for i=1,(n or #arr) do tinsert(brr, (f(arr[i]))) end
    return brr
  end

seq = function (a, b, c)
    local arr = {}
    for i=a,b,(c or 1) do tinsert(arr, i) end
    return arr
  end

nop = function () end
id  = function (...) return ... end

-- (find-luamanualw3m "#pdf-table.sort")
-- http://lua-users.org/lists/lua-l/2011-04/msg00406.html
sorted = function (tbl, lt) table.sort(tbl, lt); return tbl end

-- (find-efunctiondescr   'mapconcat)
-- (find-elnode "Index" "* mapconcat:")
mapconcat = function (f, tbl, sep, n)
    return table.concat(map(f, tbl, n), sep)
  end

maplines = function (f, bigstr)
    return mapconcat(f, splitlines(bigstr), "\n")
  end

-- «fold»  (to ".fold")
-- (find-hugsbasefile "Prelude.hs" "\nfoldl ")
-- foldl :: (a -> b -> a) -> a -> [b] -> a
foldl = function (f, a, B, i, j)
    for k=(i or 1),(j or #B) do a = f(a, B[k]) end
    return a
  end



-- «gformat»  (to ".gformat")
-- A variant of "format" that uses "string.gsub".
-- This is surprisingly useful. 8-)
--           gformat "<%1_%1>" "foo"                   --> <foo_foo>
-- mapconcat(gformat "<%1_%1>", split "foo bar", ", ") --> <foo_foo>, <bar_bar>
-- See also: (find-es "emacs" "ee-gformat")
gformat = function (fmt, pat)
    return function (str)
        return (str:gsub((pat or "^.*$"), fmt))
      end
  end



-- «each2»  (to ".each2")
-- (find-es "lua5" "each2")
-- (find-pilw3m "7.1.html" "Iterators and Closures")
each2 = function (tbl)
    local i = 1
    return function ()
        if i <= getn(tbl) then
          i = i + 2
          return tbl[i - 2], tbl[i - 1]
        end
      end
  end

-- «splitlines»  (to ".splitlines")
splitlines = function (bigstr)
    local arr = split(bigstr, "([^\n]*)\n?")
    tremove(arr)
    return arr
  end
isplitlines = function (bigstr)
    return ipairs(splitlines(bigstr))
  end

-- «translatechars»  (to ".translatechars")
-- (find-node "(coreutils)Translating")
translatechars = function (str, re, tbl)
    return (gsub(str, re, function (c) return tbl[c] or c end))
  end

-- chartranslator = function (re, tbl)
--     return function (str)
--         return gsub(str, re, function (c) return tbl[c] or c end)
--       end
--   end
-- 
-- sgmlify = chartranslator(sgmlify_re, sgmlify_table)

-- «sbeconcat»  (to ".sbeconcat")
-- Concatenate a table with strings and with begin/end pairs
-- Example:
--   sbeconcat("abfoocd"){1, 3, "FOO", 6, 8}   --> "abFOOcd"
-- This is ugly! concatbestrings, below, is much clearer.
--
sbeconcat = function (subj, f)
    f = f or function (str) return str end
    return function (table1)
        local table2, i, n = {}, 1, table.getn(table1)
        while i <= n do
          local obj = table1[i]
          if type(obj) == "string" then
            tinsert(table2, obj)
            i = i + 1
          else
	    local str = string.sub(subj, obj, table1[i+1] - 1)
            tinsert(table2, f(str))    --< sgmlify?
            i = i + 2
          end
        end
        return table.concat(table2)
      end
  end



-- «concatbestrings»  (to ".concatbestrings")
-- A "table of bestrings" is a table containing pairs of numbers
-- (begin/end pairs) and strings. Example:
--   concatbestrings("abfoocd", nil, {1, 3, "FOO", 6, 8})   --> "abFOOcd"
--         (a table of bestrings) -> \-----------------/
--
concatbestrings = function (subj, f, bestrings)
    f = f or function (s) return s end
    local table2, i = {}, 1
    while i <= #bestrings do
      local obj = bestrings[i]
      if type(obj) == "string" then
        table.insert(table2, obj)
        i = i + 1
      else
	local str = string.sub(subj, obj, bestrings[i+1] - 1)
        table.insert(table2, f(str))
        i = i + 2
      end
    end
    return table.concat(table2)
  end

curriedconcatbestrings = function (subj, f)
    return function (bestrings)
        return concatbestrings(subj, f, bestrings)
      end 
  end


-- «lpeg_togsub»  (to ".lpeg_togsub")
-- «lpeg_gsub»    (to ".lpeg_gsub")
-- A pattern that returns a string can be "Kleene-starred with
-- the least possible filling" to create a pattern that works
-- somewhat like a gsub, but that returns a table of bestrings...
-- Roughly, that would be like converting "(pat)" into
-- "\\(().-()(pat)\\)*().-()", where the "\\(...\\)" is a "shy
-- group" - i.e., its parentheses do not return a capture.
--
-- Actually this returns a sequence of captures, not a table; use Ct()
-- to pack them in to a table, and then a concatbestrings.
--
-- The logic: the pattern PosPosWord, below, works like this:
--
--    -> Pos -----+1--> Pos --> Word -->
--             ^  2
--             |  \---> Anychar ->\
--             |                  |
--             \------------------/
--
-- where each "Pos" returns a number, and "Word" returns a string.
-- The "1" and the "2" indicate the order in which the branches are
-- tried (at the "+"). The rest of the function shouldn't be hard to
-- understand.
--
-- (find-es "lua5" "lpeg-quickref")
lpeg_togsub = function (Word)
    local Pos        = lpeg.Cp()
    local AnyChar    = lpeg.P(1)
    local BeRest     = Pos * AnyChar^0 * Pos
    local PosPosWord = Pos * lpeg.P { Pos * Word + AnyChar * lpeg.V(1) }
    return PosPosWord^0 * BeRest
  end

lpeg_gsub = function (Word, subj, f)
    f = f or function (...) return ... end
    return concatbestrings(subj, f, Word:togsub():Ct():match(subj))
  end

-- Example:
-- loadlpeg()
-- Word        = lpeg.R("AZ")^1 / function (s) return "<"..s..">" end
-- = Word:gsub("abFOOcdBAR", function (s) return "_"..s.."_" end)
--         --> "_ab_<FOO>_cd_<BAR>__"


-- «lpeg_gsub_»  (to ".lpeg_gsub_")
-- An alternative (faster but more complex):
-- With gsub_ we can reuse a prebuilt :togsub():Ct() pattern,
-- without having to build it anew each time.
-- loadlpeg()
-- WordTogsubCt = Word:togsub():Ct()
-- = WordTogsubCt:gsub_("abFOOcdBAR", function (s) return "_"..s.."_" end)
--         --> "_ab_<FOO>_cd_<BAR>__"
--
lpeg_gsub_ = function (WordTogsubCt, subj, f)
    f = f or function (...) return ... end
    return concatbestrings(subj, f, WordTogsubCt:match(subj)) 
  end



-- «lpeg_balanced»  (to ".lpeg_balanced")
-- (find-angg "LUA/preproc.lua")
lpeg_balanced = function (Open, MidChars, Close)
    local Middle
    Open     = lpeg.P(Open)
    Close    = lpeg.P(Close)
    MidChars = MidChars or (1 - (Open + Close))^1
    Middle   = lpeg.P { (MidChars + Open * lpeg.V(1) * Close)^0 }
    return Open * Middle:C() * Close, Middle
  end




-- «mytraceback»  (to ".mytraceback")
-- (find-es "lua5" "xpcall" "mytraceback =")
-- (find-luamanualw3m "#pdf-xpcall")
-- (find-luamanualw3m "#pdf-debug.traceback")
-- (find-luamanualw3m "#pdf-error")
mytraceback = function (errmsg)
    io.output():flush()
    print(debug.traceback(errmsg, 2))
  end
xxcall = function (f)
    if not xpcall(f, mytraceback) then error() end
  end


-- «errorfb_line»  (to ".errorfb_line")
-- (find-es "lua5" "traceback")
-- (find-lua51file "src/ldblib.c" "{\"traceback\", db_errorfb},")
-- (find-lua51file "src/ldblib.c" "static int db_errorfb")
-- (find-lua51file "src/ldblib.c" "static int db_errorfb" "lua_getinfo")
-- http://www.lua.org/source/5.1/ldblib.c.html#db_errorfb
-- http://www.lua.org/source/5.1/ldblib.c.html#dblib
-- (find-es "lua5" "loadstring_and_eof")
-- http://lua-users.org/lists/lua-l/2011-11/msg00110.html
-- (find-es "lua5" "lua_getstack")

errorfb_line = function (ar)
    local s = "\t"
    local p = function (...) s = s..format(...) end
    p("%s:", ar.short_src)
    if ar.currentline > 0 then p("%d:", ar.currentline) end
    if ar.namewhat ~= ""  then p(" in function '%s'", ar.name) else
      if ar.what == "main" then p(" in main chunk")
      elseif ar.what == "C" or ar.what == "tail" then p(" ?")
      else p(" in function <%s:%d>", ar.short_src, ar.linedefined)
      end
    end
    return s
  end
errorfb_lines = function (a, b, step, f)
    local T = {}
    for level=a,b,(step or 1) do
      T[#T+1] = (f or errorfb_line)(debug.getinfo(level))
    end
    return table.concat(T, "\n")
  end



-- «ee_template»  (to ".ee_template")
-- (find-eev "eev-insert.el" "ee-template")
-- ee_template({a="<AA>", b="<BB>"}, "foo{a}bar{c}plic")
--   --> "foo<AA>bar{c}plic"
ee_template = function (pairs, templatestr)
    return (string.gsub(templatestr, "{([^{}]+)}", pairs))
  end

-- «ee_into»  (to ".ee_into")
-- ee_into("a b c", "<AA> <BB>")
--   --> {"a"="<AA>", "b"="<BB>"}
ee_into = function (fieldnames, data)
    if type(fieldnames) == "string" then fieldnames = split(fieldnames) end
    if type(data)       == "string" then data       = split(data)       end
    local o = {}
    for i=1,#fieldnames do o[fieldnames[i]] = data[i] end
    return o
  end

-- «chdir»  (to ".chdir")
-- 2008may23
chdir = function (dir) loadposix(); return assert(posix.chdir(ee_expand(dir))) end

-- «package.require»  (to ".package.require")
-- Make package.require consider that this file has been loaded when
-- it was loaded by LUA_INIT=@.../LUA/lua50init.lua (see the comments
-- at the top of this file) so that we can do 'require "lua50init"' or
-- 'require "edrxlib"'...
--   (find-luamanualw3m "#pdf-require")
--   (find-lua51file "")
--   (find-lua51file "src/loadlib.c" "static int ll_require ")
package.loaded.lua50init = 
  package.loaded.lua50init or "(loaded by LUA_INIT=@...)"
package.loaded.edrxlib = 
  package.loaded.edrxlib or "(loaded by LUA_INIT=@...)"


-- «userocks»  (to ".userocks")
-- (find-angg ".emacs" "luarocks")
-- (find-luamanualw3m "#pdf-require")
-- (find-luamanualw3m "#pdf-package.path")
-- (find-luamanualw3m "#pdf-package.cpath")
-- (find-fline "~/usrc/luarocks/share/lua/5.1/")
-- (find-fline "~/usrc/luarocks/lib/lua/5.1/")
userocks = function ()
    local luarocksdir = ee_expand "~/usrc/luarocks"
    package.path  = package.path ..";"..luarocksdir.."/share/lua/5.1/?.lua"
    package.cpath = package.cpath..";"..luarocksdir.."/lib/lua/5.1/?.so"
    req = function (pkgname) return function () require(pkgname) end end
    loadposix  = req "posix"
    load_posix = req "posix"
  end

-- «loadblogme3» (to ".loadblogme3")
loadblogme3 = function ()
    blogmedir = ee_expand "~/blogme3/"
    ee_dofile "~/blogme3/blogme3.lua"
  end

-- «icollect» (to ".icollect")
-- (find-es "lua5" "icollect")
-- http://lua-users.org/lists/lua-l/2012-12/msg00661.html
-- http://lua-users.org/lists/lua-l/2012-12/msg00713.html
-- http://lua-users.org/lists/lua-l/2012-12/msg00739.html
icollect_helper = function (t, i, n, f, s, var_1, ...)
    if var_1 ~= nil then
      t[i] = select(n, var_1, ...)
      return icollect_helper(t, i+1, n, f, s, f(s, var_1))
    end
    return t, i-1
  end
icollect = function (n, f, s, var)
    return icollect_helper({}, 1, n, f, s, f(s, var))
  end



-- «over0»  (to ".over0")
-- Example:
-- A = {a=22}
-- B = over(A, {b=33})
-- PP(B, A, B.b, B.a)
--   --> {"b"=33} {"a"=22} 33 22
--[[
over = function (bottomtable, toptable)
    return setmetatable(toptable or {}, {__index = bottomtable})
  end
--]]

-- «interactor»  (to ".interactor")
-- This is very new, incomplete, etc; I'm rewriting it.
-- The new version is at: (find-angg "LUA/reader.lua")
-- (find-es "lua5" "interactor")
-- (find-TH "repl")
-- Example:
--   compiled, errmsg = interactor():read(".>"):complete()
-- Some explanations:
--   interactor() returns a table that is "over" interactor_metatable,
--   I:read(".>") reads the first line with prompt ".>",
--   I:complete() tries to run loadstring(I:body()); while that is
--                not a complete Lua chunk, keep reading. When either
--                the input becomes complete or another error besides
--                "incomplete" occurs, return I.compiled and I.errmsg.
-- Notes:
--   On "abort" (i.e., either reading a line with just ".", or getting
--     an eof), I:complete() returns two nils. In all the other cases
--     exactly one of I.compiled, I.errmsg will be non-nil, and
--     assert(I.compiled, I.errmsg)() would do what's expected.
--   I'm planning the treat an abort at the first line specially -
--     meaning "return from the interactor loop".
--   This doesn't do much at this moment - I'm experimenting with loops
--     around interactor():read(".>"):complete(), but the code for
--     these experiments is elsewhere...
--
--[[
interactor_metatable = {
  concat = function (self) return table.concat(self, "\n") end,
  body   = function (self) return self:concat() end,  -- overriddable
  prompt = function (self) return ">> " end,          -- overriddable
  abort = function (self) end,                        -- overriddable; message
  load = function (self)
      if self.line then
        self.compiled, self.errmsg = loadstring(self:body())
        self.incomplete = self.errmsg and
          string.find(self.errmsg, ": unexpected symbol near '<eof>'$")
      else
        self.compiled, self.errmsg, self.incomplete = nil, nil, nil
      end
      return self
    end,
  read = function (self, prompt)
      io.write(prompt or self:prompt())
      self.line = io.read()
      if self.line == "." then self:abort(); self.line = nil end
      if self.line then table.insert(self, self.line) end
      return self
    end,
  complete = function (self)
      while self.line and self.incomplete do self:read(); self:load() end
      return self
    end,
}

interactor = function () return over(interactor_metatable) end
--]]



-- «eoo» (to ".eoo")
-- (find-dn5 "eoo.lua")
Class = {
    type   = "Class",
    __call = function (class, o) return setmetatable(o, class) end,
  }
setmetatable(Class, Class)

-- «over» (to ".over")
-- (find-es "lua5" "over")
over = function (B)
    return function (A)
        return setmetatable(A, {__index=B})
      end
  end
Over = function (class)
    return over(class.__index)
  end

-- «follow» (to ".follow")
follow = function (o, str)
    local w, rest = str:match("(%S+)%s*(.*)")
    if not w then return o end
    if w == "()" then return follow(o(), rest) end
    if w == "{}" then return follow(o{}, rest) end
    if w == "mt" then return follow(getmetatable(o), rest) end
    return follow(o[w], rest)
  end

-- «NamedFunction» (to ".NamedFunction")
-- (find-es "lua5" "NamedFunction")
NamedFunction = Class {
  type    = "NamedFunction",
  __tostring = function (o) return o.name end,
  __call     = function (o, ...) return o.f(...) end,
  __index = {
  },
}
lambda = function (str)
    local vars,rest = str:match "^ *([%w_,]*)[ .:]*(.-) *$"
    local body = rest:gsub("=>", " return ")
    local code = "return function ("..vars..")\n"..body.."\nend"
    local name = "("..vars..": "..rest..")"
    local f = assert(loadstring(code))()
    -- return NamedFunction {name=name, f=f}
    return NamedFunction {name=name, code=code, f=f}
  end

-- «Repl» (to ".Repl")
-- (find-es "lua5" "Repl")
Repl = Class {
  type    = "Repl",
  __index = {
    incompletep0 = function (r, str)
        return str:find(": unexpected symbol near '<eof>'$")
      end,
    incompletep = function (r, str)
        local f, err = loadstring(str)
        return f == nil and r:incompletep0(err)
      end,
    read0 = function (r, prompt) io.write(prompt); return io.read() end,
    read1 = function (r) return r:read0 ">>> "  end,
    read2 = function (r) return r:read0 "... " end,
    complete = function (r, str)
	while r:incompletep(str) do str = str.."\n"..r:read2() end
	return str
      end,
    split = function (r, str) return str:match "^(=?=?)(.*)$" end,
    printers = {[""]=id, ["="]=print, ["=="]=PP},
    prepends = {[""]="", ["="]="return ", ["=="]="return "},
    read_ = function (r) return r:complete(r:read1()) end,
    read = function (r, str00)
        local prefix, str0 = r:split(str00 or r:read1())
        local printer, prepend = r.printers[prefix], r.prepends[prefix]
        local str = r:complete(prepend..str0)
        return printer, str
      end,
    evalprint = function (r, printer, str)
        local f, err = loadstring(str)
        return printer(f())
      end,
    rep = function (r, str00) return r:evalprint(r:read(str00)) end,
    repp = function (r)
        local str00 = r:read1()
        if str00 == "!" then return false end
        r:rep(str00)
        return true
      end,
    repl = function (r) while r:repp() do end end,
  },
}


-- «replaceranges» (to ".replaceranges")
-- str = "(foo bar)"
-- ranges = {{6,9,"BAR"}, {2,5, string.upper}}
-- = replaceranges(str, ranges)
replaceranges_ = function (str, ranges, spos, rpos, f)
    local range = ranges[rpos]
    if not range then return str:sub(spos) end
    local a, b, s_or_f = range[1], range[2], range[3]
    local s = (type(s_or_f) == "string")
                and s_or_f
                or  s_or_f(str:sub(a, b-1))
    return f(str:sub(spos, a-1)) .. s ..
           replaceranges_(str, ranges, b, rpos+1, f)
  end
replaceranges = function (str, ranges, f)
    table.sort(ranges, function (a, b) return a[1]<b[1] end)
    return replaceranges_(str, ranges, 1, 1, f or id)
  end


-- «Sexp» (to ".Sexp")
-- 2013jan16; to be moved to blogme3 / blogme4
string.revgsub = function (str, ...)
    return str:reverse():gsub(...):reverse()
  end
underlinify = function (s)
    return s:sub(1, 1)..string.rep("_", #s-2)..s:sub(#s)
  end

intro_url = function (stem)
    return anggurl("eev-intros/find-"..stem.."-intro.html")
  end

angg_url  = function (fname) return "../"..fname end
intro_url = function (stem) return string.upper(stem) end
isdir     = function (fname) return fname=="" or fname:sub(-1)=="/" end
suffixing = function (fname, suffix)
    return isdir(fname) and fname or fname..suffix
  end
meta_find_angg = function (anggdir, suffix, intro_stem)
    return function (fname, tag)
        local target = fname and
          angg_url(anggdir)..suffixing(fname, suffix or ".html")
        if target and tag then target = target.."#"..tag end
        return target, intro_url(intro_stem or "code-c-d")
      end
  end

elisp = {}
elisp["find-angg"] = meta_find_angg("")
elisp["find-es"]   = meta_find_angg("e/", ".e.html")

elispSPECIAL = {}

Sexp = Class {
  type    = "Sexp",
  __index = {
    getsexp = function (sexp)
        local line = sexp.line
        if line:sub(#line) ~= ")" then return end
        local len = #line
        local skel = line:gsub("\\.", "__")              -- backslash+c -> __
        skel = skel:revgsub("\"[^\"]*\"", underlinify)   -- "abc" -> "___"
        skel = skel:sub(1, len-1):revgsub("%b)(", underlinify)..skel:sub(len)
        local revsexp = line:reverse():match("^%b)(")
        if not revsexp then return end
        sexp.sexp = revsexp:reverse()
        sexp.pre  = line:sub(1, len-#sexp.sexp)
        -- sexp.sexpskel = skel:sub(#sexp.pre+1)
        sexp.midskel = skel:sub(#sexp.pre+2, len-1)
        sexp.head    = sexp.midskel:match("^%S*")
        return sexp.head
      end,
    getsexpargs = function (sexp)
        local n = 0
        sexp.ranges = {}
        for i,j in sexp.midskel:gmatch("()%S+()") do
	  n = n + 1
          sexp[n] = sexp.sexp:sub(i+1, j)
          sexp.ranges[n] = {i+1, j+1}
        end
      end,
    string = function (sexp, n)
        return sexp[n] and sexp[n]:match '^"(.*)"$'
      end,
    --
    Q = function (text) return text end,
    hrefto = function (sexp, url)
        return function (text)
	    return '<a href="'..url..'">'..sexp.Q(text)..'</a>'
	  end
      end,
    setrange = function (sexp, a, b, s_or_f)
          table.insert(sexp.htmlranges, {a, b, s_or_f})
      end,
    sethelp = function (sexp, url)
        local r = sexp.ranges[1]
	if url then sexp:setrange(r[1], r[2], sexp:hrefto(url)) end
        -- table.insert(sexp.htmlranges, {r[1], r[2], sexp:hrefto(url)})
	-- end
      end,
    settarget = function (sexp, url)
        local len = #sexp.sexp
        if url then sexp:setrange(len-1, len+1, sexp:hrefto(url)) end
        -- table.insert(sexp.htmlranges, {len-1, len+1, sexp:hrefto(url)})
	-- end
      end,
    tohtml0 = function (sexp)
        return replaceranges(sexp.sexp, sexp.htmlranges or {}, sexp.Q)
      end,
    getsexphtml = function (sexp)
	sexp.htmlranges = {}
        if not sexp.head then return end
	if elispSPECIAL[sexp.head] then    -- for all kinds of special hacks 
	  elispSPECIAL[sexp.head](sexp)
	  return
	end
        local find_aaa = elisp[sexp.head]
	if not find_aaa then return end
	sexp:getsexpargs()
	sexp.target, sexp.help = find_aaa(sexp:string(2), sexp:string(3))
  	sexp:sethelp(sexp.help)
	sexp:settarget(sexp.target)
	sexp.sexphtml = sexp:tohtml0()
      end,
    getlinehtml = function (sexp)
        if sexp:getsexp() then sexp:getsexphtml() end
        if sexp.sexphtml
        then sexp.linehtml = sexp.Q(sexp.pre)..sexp.sexphtml
        else sexp.linehtml = sexp.Q(sexp.line)
	end
	return sexp.linehtml
      end,
  },
}

-- «youtube_split» (to ".youtube_split")
-- I was using this at too many places - including one-shot programs...
-- (find-angg "LUA/youtube-tags.lua")
-- (find-angg "LUA/youtube.lua")
youtube_make_url = function (hash, time)
    return "http://www.youtube.com/watch?v=" .. hash
  end
youtube_split_url0 = function (li)
    local a, url, b, title, c = li:match "^(.-)(https?://%S*)(%s*)(.-)(%s*)$"
    if not url then return end
    local hash, time
    for key,value in url:gmatch "[?&](%w*)=([^?&#'()]*)" do
      if key == "v" then hash = value end
      if key == "t" then time = value end  -- not being used now
    end
    if not hash then return end
    return a, hash, b, title, c
  end
youtube_split_url = function (li)
    local a, hash, b, title, c = youtube_split_url0(li)
    if a then return hash, youtube_make_url(hash), title end
  end



Blogme = Class {
  type    = "Blogme",
  __index = {
    ps = function (bme)  -- parse space chars
        local s, p = bme.str:match("^([ \t\n]+)()", bme.pos)
        if s then bme.pos = p; return s end
      end,
    pw = function (bme)  -- parse word chars (i.e., no spaces, no brackets)
        local s, p = bme.str:match("^([^%[%] \t\n]+)()", bme.pos)
        if s then bme.pos = p; return s end
      end,
    pr = function (bme)  -- parse regular chars (i.e., no brackets)
        local s, p = bme.str:match("^([^%[%]]+)()", bme.pos)
        if s then bme.pos = p; return s end
      end,
    pb0 = function (bme) -- parse bracket (without evaluation)
        local b, e = bme.str:match("^()%b[]()", bme.pos)
        if b then bme.pos = e; return b+1, e-1 end
      end,
    pqarg0 = function (bme) -- parse a quoted argument (withot evaluation)
        bme:ps()
        local b = bme.pos
        while bme:pb0() or bme:pw() do end
        return b, bme.pos
      end,
    sub = function (bme, b, e) return b and bme.str:sub(b, e-1) end,
  },
}






-- Local Variables:
-- coding:               raw-text-unix
-- ee-anchor-format:     "«%s»"
-- End: