From 29282b5a7d43e8b5ce12b0ec4b7a7620c19a67b6 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Mon, 9 Feb 2015 19:44:12 +0200 Subject: proper escaping for back-end addresses --- aconf/path/address/init.lua | 33 +++++++++ aconf/path/address/special.lua | 35 +++++++++ aconf/path/base.lua | 159 +++++++++++++++++++++++++++++++++++++++++ aconf/path/init.lua | 6 ++ 4 files changed, 233 insertions(+) create mode 100644 aconf/path/address/init.lua create mode 100644 aconf/path/address/special.lua create mode 100644 aconf/path/base.lua create mode 100644 aconf/path/init.lua (limited to 'aconf/path') diff --git a/aconf/path/address/init.lua b/aconf/path/address/init.lua new file mode 100644 index 0000000..c62487f --- /dev/null +++ b/aconf/path/address/init.lua @@ -0,0 +1,33 @@ +--[[ +Copyright (c) 2012-2015 Kaarle Ritvanen +See LICENSE file for license details +--]] + +local object = require('aconf.object') +local class = object.class + +local special = require('aconf.path.address.special') +local base = require('aconf.path.base') + + +local M = { + special={ + enum_keys=special.EnumKeys(), + value=special.Value(), + value_equals=special.ValueEquals + } +} + +local _special = {['#']=M.special.enum_keys, ['&']=M.special.value} + + +local AddressSyntax = class(base.Syntax) + +function AddressSyntax:get_special(comp) + if comp:sub(1, 1) == '@' then + return special.ValueEquals(#comp > 1 and comp:sub(2, -1) or nil) + end + return _special[comp] or object.super(self, AddressSyntax):get_special(comp) +end + +return AddressSyntax():export(M) diff --git a/aconf/path/address/special.lua b/aconf/path/address/special.lua new file mode 100644 index 0000000..ab94fce --- /dev/null +++ b/aconf/path/address/special.lua @@ -0,0 +1,35 @@ +--[[ +Copyright (c) 2012-2015 Kaarle Ritvanen +See LICENSE file for license details +--]] + +local M = {} + + +local object = require('aconf.object') +local class = object.class + + +M.SpecialMode = class(require('aconf.path.base').Special) + +M.Value = class(M.SpecialMode) +M.Value.symbol = '&' + +M.Selector = class(M.SpecialMode) + +M.EnumKeys = class(M.Selector) +M.EnumKeys.symbol = '#' + + +M.ValueEquals = class(M.Selector) + +M.ValueEquals.symbol = '@' + +function M.ValueEquals:init(key) self.key = key end + +function M.ValueEquals:tostring() + return object.super(self, M.ValueEquals):tostring()..(self.key or '') +end + + +return M diff --git a/aconf/path/base.lua b/aconf/path/base.lua new file mode 100644 index 0000000..4e2bf8f --- /dev/null +++ b/aconf/path/base.lua @@ -0,0 +1,159 @@ +--[[ +Copyright (c) 2012-2015 Kaarle Ritvanen +See LICENSE file for license details +--]] + +local M = {} + + +local object = require('aconf.object') +local class = object.class + +local map = require('aconf.util').map + + +M.Special = class() +function M.Special:tostring() return self.symbol end + +local Up = class(M.Special) +Up.symbol = '..' + +local Wildcard = class(M.Special) +Wildcard.symbol = '*' + + + +M.Syntax = class() + +M.Syntax.up = Up() +M.Syntax.wildcard = Wildcard() + + +function M.Syntax:get_special(comp) + return ({['..']=M.Syntax.up, ['*']=M.Syntax.wildcard})[comp] +end + + +function M.Syntax:is_absolute(path) return path:sub(1, 1) == '/' end + + +function M.Syntax:escape(comp) + if type(comp) == 'table' then + if object.isinstance(object.toinstance(comp), M.Special) then + return comp:tostring() + end + end + if type(comp) == 'number' then return tostring(comp) end + local res = comp:gsub('([\\/])', '\\%1') + return (self:get_special(res) or tonumber(res)) and '\\'..res or res +end + + +function M.Syntax:rawjoin(p1, p2, ...) + if not p2 then return p1 end + if not self:is_absolute(p2) then p2 = '/'..p2 end + return self:rawjoin((p1 == '/' and '' or p1)..p2, ...) +end + +function M.Syntax:join(parent, ...) + local args = map(function(c) return self:escape(c) end, {...}) + if parent > '' then table.insert(args, 1, parent) end + return self:rawjoin(table.unpack(args)) +end + + +function M.Syntax:split(path) + local res = {} + local comp = '' + local escaped + + local function merge(s) + if s > '' then + table.insert( + res, not escaped and (self:get_special(s) or tonumber(s)) or s + ) + end + end + + while true do + local prefix, sep, suffix = path:match('([^\\/]*)([\\/])(.*)') + if not prefix then + merge(comp..path) + return res + end + + comp = comp..prefix + if sep == '\\' then + comp = comp..suffix:sub(1, 1) + escaped = true + path = suffix:sub(2, -1) + else + merge(comp) + comp = '' + escaped = false + path = suffix + end + end +end + + +function M.Syntax:is_unique(path) + for _, comp in ipairs(self:split(path)) do + if comp == self.wildcard then return false end + end + return true +end + +function M.Syntax:is_subordinate(p1, p2) + p1 = self:split(p1) + for i, comp in ipairs(self:split(p2)) do + if p1[i] ~= comp then return false end + end + return true +end + + +function M.Syntax:to_absolute(path, base) + if not self:is_absolute(path) then + path = base..(base ~= '/' and '/' or '')..path + end + local comps = self:split(path) + local i = 1 + while i <= #comps do + if comps[i] == self.up then + if i == 1 then error('Invalid path: '..path) end + table.remove(comps, i - 1) + table.remove(comps, i - 1) + i = i - 1 + else i = i + 1 end + end + return self:join('/', table.unpack(comps)) +end + + +function M.Syntax:parent(path) + local comps = self:split(path) + table.remove(comps) + return self:join('/', table.unpack(comps)) +end + +function M.Syntax:name(path) + local comps = self:split(path) + return comps[#comps] +end + + +function M.Syntax:export(mod) + if not mod then mod = {} end + for k, v in pairs(M.Syntax) do + if k ~= 'export' then + mod[k] = type(v) == 'function' and function(...) + return v(self, ...) + end or v + end + end + return mod +end + + +return M diff --git a/aconf/path/init.lua b/aconf/path/init.lua new file mode 100644 index 0000000..5ac9642 --- /dev/null +++ b/aconf/path/init.lua @@ -0,0 +1,6 @@ +--[[ +Copyright (c) 2012-2015 Kaarle Ritvanen +See LICENSE file for license details +--]] + +return require('aconf.path.base').Syntax():export() -- cgit v1.2.3