1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
--[[
Copyright (c) 2012-2013 Kaarle Ritvanen
See LICENSE file for license details
--]]
local topology = require('acf2.model.root').topology
local pth = require('acf2.path')
local util = require('acf2.util')
local copy = util.copy
local function aug_path(path) return pth.join('/files', unpack(path)) end
local function strip_name(name)
return type(name) == 'string' and name:match('^[^][/=)%s]+') or name
end
local function ipath(path, index) return path..'['..index..']' end
local backend = require('acf2.object').class()
function backend:init() self.aug = require('augeas').init() end
function backend:find(path, leaf)
util.map(
function(comp)
assert(
comp == strip_name(comp) and (
type(comp) == 'number' or not comp:match('^%.+$')
)
)
end,
path
)
local res = aug_path(path)
if #self.aug:match(res) == 0 and #path > 1 and leaf then
local index = path[#path]
if type(index) == 'number' then
local ppath = copy(path)
table.remove(ppath)
ppath = aug_path(ppath)
if #self.aug:match(ppath) > 0 and #self.aug:match(
ppath..'/*'
) == 0 then
return ipath(ppath, index), ppath, index
end
end
end
return res
end
function backend:get(path, top)
local tpe = top and top.type
local leaf = tpe and tpe ~= 'table'
local apath, mvpath = self:find(path, leaf or not tpe)
local matches = self.aug:match(apath)
if mvpath then
assert(#matches < 2)
leaf = true
end
if #matches == 0 then return end
if #matches > 1 then
assert(not leaf)
local res = {}
path = copy(path)
for i, _ in ipairs(matches) do
table.insert(path, i)
if self:get(path) then table.insert(res, i) end
table.remove(path)
end
return res
end
local value = self.aug:get(matches[1])
if value then return tpe == 'table' and {1} or value end
if leaf then return end
local names = {}
for _, child in ipairs(self.aug:match(apath..'/*')) do
names[strip_name(pth.name(child))] = true
end
return util.keys(names)
end
function backend:set(mods)
local gcpaths = {}
for _, mod in ipairs(mods) do
local path, value = unpack(mod)
local delete = value == nil
self.aug:rm(aug_path(path)..(delete and '' or '/*'))
local apath, mvpath, index = self:find(path, type(value) ~= 'table')
local mpath = mvpath or apath
if not delete then
if #self.aug:match(mpath) == 0 then
local function order(path)
return topology('/augeas'..path).order
end
local ord = order(pth.join('/', unpack(path)))
for _, sibling in ipairs(self.aug:match(pth.parent(mpath)..'/*')) do
if order(strip_name(sibling):sub(7, -1)) > ord then
self.aug:insert(sibling, pth.name(mpath), true)
break
end
end
end
if mvpath then
local size = #self.aug:match(mvpath)
while size < index do
self.aug:insert(ipath(mvpath, size), pth.name(mvpath))
size = size + 1
end
end
end
if type(value) == 'table' then value = nil end
if not delete or mvpath then self.aug:set(apath, value)
elseif apath > '/' then apath = pth.parent(apath) end
if delete or value == '' then gcpaths[mpath] = true end
end
local function gc(path)
if path == '/' or #self.aug:match(path..'/*') > 0 then return end
if self.aug:rm(path.."[. = '']") > 0 then gc(pth.parent(path)) end
end
for p, _ in pairs(gcpaths) do gc(p) end
if self.aug:save() ~= 0 then
print('Augeas save failed')
for _, ep in ipairs(self.aug:match('/augeas//error')) do
print(ep, self.aug:get(ep))
end
assert(false)
end
end
return backend
|