File

util/xml.lua @ 9117:a19fdc6e4f09

util.pubsub: Apply defaults metatable before config check (thanks pep.) Makes it so that the callback sees the default if it’s not in the form, which makes it easier to validate.
author Kim Alvefur <zash@zash.se>
date Sun, 05 Aug 2018 15:17:00 +0200
parent 8555:4f0f5b49bb03
child 11127:1d9cd1abc660
child 12181:783056b4e448
line wrap: on
line source


local st = require "util.stanza";
local lxp = require "lxp";
local t_insert = table.insert;
local t_remove = table.remove;

local _ENV = nil;
-- luacheck: std none

local parse_xml = (function()
	local ns_prefixes = {
		["http://www.w3.org/XML/1998/namespace"] = "xml";
	};
	local ns_separator = "\1";
	local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
	return function(xml)
		--luacheck: ignore 212/self
		local handler = {};
		local stanza = st.stanza("root");
		local namespaces = {};
		local prefixes = {};
		function handler:StartNamespaceDecl(prefix, url)
			if prefix ~= nil then
				t_insert(namespaces, url);
				t_insert(prefixes, prefix);
			end
		end
		function handler:EndNamespaceDecl(prefix)
			if prefix ~= nil then
				-- we depend on each StartNamespaceDecl having a paired EndNamespaceDecl
				t_remove(namespaces);
				t_remove(prefixes);
			end
		end
		function handler:StartElement(tagname, attr)
			local curr_ns,name = tagname:match(ns_pattern);
			if name == "" then
				curr_ns, name = "", curr_ns;
			end
			if curr_ns ~= "" then
				attr.xmlns = curr_ns;
			end
			for i=1,#attr do
				local k = attr[i];
				attr[i] = nil;
				local ns, nm = k:match(ns_pattern);
				if nm ~= "" then
					ns = ns_prefixes[ns];
					if ns then
						attr[ns..":"..nm] = attr[k];
						attr[k] = nil;
					end
				end
			end
			local n = {}
			for i=1,#namespaces do
				n[prefixes[i]] = namespaces[i];
			end
			stanza:tag(name, attr, n);
		end
		function handler:CharacterData(data)
			stanza:text(data);
		end
		function handler:EndElement()
			stanza:up();
		end
		local parser = lxp.new(handler, "\1");
		local ok, err, line, col = parser:parse(xml);
		if ok then ok, err, line, col = parser:parse(); end
		--parser:close();
		if ok then
			return stanza.tags[1];
		else
			return ok, err.." (line "..line..", col "..col..")";
		end
	end;
end)();

return {
	parse = parse_xml;
};