summaryrefslogtreecommitdiff
path: root/xmpp/xml.lua
diff options
context:
space:
mode:
Diffstat (limited to 'xmpp/xml.lua')
-rw-r--r--xmpp/xml.lua173
1 files changed, 173 insertions, 0 deletions
diff --git a/xmpp/xml.lua b/xmpp/xml.lua
new file mode 100644
index 0000000..68fa741
--- /dev/null
+++ b/xmpp/xml.lua
@@ -0,0 +1,173 @@
+-- originally from http://lua-users.org/wiki/LuaXml
+-- modified by me a bit
+
+
+entity_escapes = {
+ ["<"]="&lt;",
+ [">"]="&gt;",
+ ["&"]="&amp;",
+ ['"']="&quot;",
+ ["'"]="&apos;"
+}
+entity_unescapes = {}
+for k,v in pairs(entity_escapes) do entity_unescapes[v]=k end
+
+function escape(s)
+ return s:gsub("[<>&'\"]",entity_escapes)
+end
+function unescape(s)
+ return s:gsub("&[a-z]+;",entity_unescapes)
+end
+
+local function parseargs(s)
+ local arg = {}
+ string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
+ arg[w] = a
+ end)
+ return arg
+end
+
+local function tag(x)
+ return setmetatable(x, {__call=function(x, label)
+ -- search for child with given label
+ for _,c in ipairs(x) do
+ if c.label == label then
+ return c
+ end
+ end
+ return nil
+ end})
+end
+
+local psingle
+
+local function pmulti(s, i, parent)
+ ::again::
+ local nexti, child = psingle(s, i)
+ if child.close and child.label == parent.label then
+ return nexti, parent
+ else
+ table.insert(parent, child)
+ i = nexti
+ goto again
+ end
+end
+
+psingle = function(s, i)
+ i = i or 1
+ local ts,j,c,label,xarg,empty = s:find("<(%/?)([%w:]+)(.-)(%/?)>", i)
+ if not ts then
+ local rest = s:sub(i)
+ if rest:find("<",i) then
+ error('ill formed (eof?)')
+ elseif #rest == 0 then
+ error('empty string')
+ else
+ return i+#rest, unescape(rest)
+ end
+ end
+ local nexti = j+1
+
+ local pretext = s:sub(i, ts-1)
+ if not pretext:find("^%s*$") then -- not entirely whitespace
+ return ts, unescape(pretext)
+ end
+
+ if empty == "/" then
+ return nexti, tag{label=label, xarg=parseargs(xarg), empty=true}
+ elseif c == "" then -- start tag
+ return pmulti(s, nexti, tag{label=label, xarg=parseargs(xarg)})
+ else -- end tag
+ return nexti, tag{label=label, close=true}
+ end
+end
+
+local wrap
+do
+ local _, cqa = pcall(require, 'cqueues.auxlib')
+ wrap = cqa and cqa.wrap or coroutine.wrap
+end
+
+local function stanzae(getmore)
+ return wrap(function()
+ local buf = ''
+ while true do
+ local ok, ni, el = pcall(psingle, buf)
+ if ok then
+ coroutine.yield(el)
+ buf = buf:sub(ni)
+ else
+ local more = assert(getmore())
+ buf = buf .. more
+ end
+ end
+ end)
+end
+
+
+local safestr_mt = {name='SAFESTR', __tostring=function(x) return x.s end}
+local function safestr(s) return setmetatable({s=s}, safestr_mt) end
+
+local function _xmlify(x)
+ if getmetatable(x) == safestr_mt then
+ return x
+ elseif type(x) == 'string' then
+ return safestr(escape(x))
+ elseif type(x) == 'table' then -- must be a tag
+ local function argstr(t)
+ local argstring = ''
+ for k,v in pairs(t) do
+ argstring = argstring .. (" %s='%s'"):format(k, _xmlify(v))
+ end
+ return argstring
+ end
+ if x.empty then
+ return safestr(("<%s%s/>"):format(x.label, argstr(x.xarg)))
+ else
+ local open = ("<%s%s>"):format(x.label, argstr(x.xarg))
+ local close = ("</%s>"):format(x.label)
+ local children = {}
+ for _,child in ipairs(x) do
+ table.insert(children, tostring(_xmlify(child)))
+ end
+ return safestr(open .. table.concat(children, '') .. close)
+ end
+ end
+end
+
+local function xmlify(...)
+ local n = select('#',...)
+ if n <= 1 then return _xmlify(...) end
+
+ local out = {}
+ for i = 1, n do
+ out[i] = tostring(_xmlify(select(i, ...)))
+ end
+ return table.concat(out, '\n')
+end
+
+-- dsl for input to xmlify. doesn't do escaping or anything.
+-- THERE IS NO ESCAPE
+local X = setmetatable({}, {__index=function(_,label)
+ return function(tab)
+ local out = {}
+ local xarg = {}
+ local empty = true
+
+ for k, v in pairs(tab) do
+ if type(k) == 'number' then -- child
+ empty = nil
+ out[k] = v
+ elseif type(k) == 'string' then -- attrib
+ xarg[k] = v
+ end
+ end
+
+ out.label = label
+ out.xarg = xarg
+ out.empty = empty
+ return out
+ end
+end})
+
+return { psingle = psingle, stanzae = stanzae, wrap=wrap, xmlify=xmlify, X=X }