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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
--- Utility module for loading files into tables and
-- saving tables into files.
local persist = {}
local core = require("luarocks.core.persist")
local util = require("luarocks.util")
local dir = require("luarocks.dir")
local fs = require("luarocks.fs")
persist.run_file = core.run_file
persist.load_into_table = core.load_into_table
local write_table
--- Write a value as Lua code.
-- This function handles only numbers and strings, invoking write_table
-- to write tables.
-- @param out table or userdata: a writer object supporting :write() method.
-- @param v: the value to be written.
-- @param level number: the indentation level
-- @param sub_order table: optional prioritization table
-- @see write_table
function persist.write_value(out, v, level, sub_order)
if type(v) == "table" then
level = level or 0
write_table(out, v, level + 1, sub_order)
elseif type(v) == "string" then
if v:match("[\r\n]") then
local open, close = "[[", "]]"
local equals = 0
local v_with_bracket = v.."]"
while v_with_bracket:find(close, 1, true) do
equals = equals + 1
local eqs = ("="):rep(equals)
open, close = "["..eqs.."[", "]"..eqs.."]"
end
out:write(open.."\n"..v..close)
else
out:write(("%q"):format(v))
end
else
out:write(tostring(v))
end
end
local is_valid_plain_key
do
local keywords = {
["and"] = true,
["break"] = true,
["do"] = true,
["else"] = true,
["elseif"] = true,
["end"] = true,
["false"] = true,
["for"] = true,
["function"] = true,
["goto"] = true,
["if"] = true,
["in"] = true,
["local"] = true,
["nil"] = true,
["not"] = true,
["or"] = true,
["repeat"] = true,
["return"] = true,
["then"] = true,
["true"] = true,
["until"] = true,
["while"] = true,
}
function is_valid_plain_key(k)
return type(k) == "string"
and k:match("^[a-zA-Z_][a-zA-Z0-9_]*$")
and not keywords[k]
end
end
local function write_table_key_assignment(out, k, level)
if is_valid_plain_key(k) then
out:write(k)
else
out:write("[")
persist.write_value(out, k, level)
out:write("]")
end
out:write(" = ")
end
--- Write a table as Lua code in curly brackets notation to a writer object.
-- Only numbers, strings and tables (containing numbers, strings
-- or other recursively processed tables) are supported.
-- @param out table or userdata: a writer object supporting :write() method.
-- @param tbl table: the table to be written.
-- @param level number: the indentation level
-- @param field_order table: optional prioritization table
write_table = function(out, tbl, level, field_order)
out:write("{")
local sep = "\n"
local indentation = " "
local indent = true
local i = 1
for k, v, sub_order in util.sortedpairs(tbl, field_order) do
out:write(sep)
if indent then
for _ = 1, level do out:write(indentation) end
end
if k == i then
i = i + 1
else
write_table_key_assignment(out, k, level)
end
persist.write_value(out, v, level, sub_order)
if type(v) == "number" then
sep = ", "
indent = false
else
sep = ",\n"
indent = true
end
end
if sep ~= "\n" then
out:write("\n")
for _ = 1, level - 1 do out:write(indentation) end
end
out:write("}")
end
--- Write a table as series of assignments to a writer object.
-- @param out table or userdata: a writer object supporting :write() method.
-- @param tbl table: the table to be written.
-- @param field_order table: optional prioritization table
-- @return true if successful; nil and error message if failed.
local function write_table_as_assignments(out, tbl, field_order)
for k, v, sub_order in util.sortedpairs(tbl, field_order) do
if not is_valid_plain_key(k) then
return nil, "cannot store '"..tostring(k).."' as a plain key."
end
out:write(k.." = ")
persist.write_value(out, v, 0, sub_order)
out:write("\n")
end
return true
end
--- Write a table using Lua table syntax to a writer object.
-- @param out table or userdata: a writer object supporting :write() method.
-- @param tbl table: the table to be written.
local function write_table_as_table(out, tbl)
out:write("return {\n")
for k, v, sub_order in util.sortedpairs(tbl) do
out:write(" ")
write_table_key_assignment(out, k, 1)
persist.write_value(out, v, 1, sub_order)
out:write(",\n")
end
out:write("}\n")
end
--- Save the contents of a table to a string.
-- Each element of the table is saved as a global assignment.
-- Only numbers, strings and tables (containing numbers, strings
-- or other recursively processed tables) are supported.
-- @param tbl table: the table containing the data to be written
-- @param field_order table: an optional array indicating the order of top-level fields.
-- @return persisted data as string; or nil and an error message
function persist.save_from_table_to_string(tbl, field_order)
local out = {buffer = {}}
function out:write(data) table.insert(self.buffer, data) end
local ok, err = write_table_as_assignments(out, tbl, field_order)
if not ok then
return nil, err
end
return table.concat(out.buffer)
end
--- Save the contents of a table in a file.
-- Each element of the table is saved as a global assignment.
-- Only numbers, strings and tables (containing numbers, strings
-- or other recursively processed tables) are supported.
-- @param filename string: the output filename
-- @param tbl table: the table containing the data to be written
-- @param field_order table: an optional array indicating the order of top-level fields.
-- @return boolean or (nil, string): true if successful, or nil and a
-- message in case of errors.
function persist.save_from_table(filename, tbl, field_order)
local prefix = dir.dir_name(filename)
fs.make_dir(prefix)
local out = io.open(filename, "w")
if not out then
return nil, "Cannot create file at "..filename
end
local ok, err = write_table_as_assignments(out, tbl, field_order)
out:close()
if not ok then
return nil, err
end
return true
end
--- Save the contents of a table as a module.
-- The module contains a 'return' statement that returns the table.
-- Only numbers, strings and tables (containing numbers, strings
-- or other recursively processed tables) are supported.
-- @param filename string: the output filename
-- @param tbl table: the table containing the data to be written
-- @return boolean or (nil, string): true if successful, or nil and a
-- message in case of errors.
function persist.save_as_module(filename, tbl)
local out = io.open(filename, "w")
if not out then
return nil, "Cannot create file at "..filename
end
write_table_as_table(out, tbl)
out:close()
return true
end
function persist.load_config_file_if_basic(filename, cfg)
local env = {
home = cfg.home
}
local result, err, errcode = persist.load_into_table(filename, env)
if errcode == "load" or errcode == "run" then
-- bad config file or depends on env, so error out
return nil, "Could not read existing config file " .. filename
end
local tbl
if errcode == "open" then
-- could not open, maybe file does not exist
tbl = {}
else
tbl = result
tbl.home = nil
end
return tbl
end
function persist.save_default_lua_version(prefix, lua_version)
local ok, err = fs.make_dir(prefix)
if not ok then
return nil, err
end
local fd, err = io.open(dir.path(prefix, "default-lua-version.lua"), "w")
if not fd then
return nil, err
end
fd:write('return "' .. lua_version .. '"\n')
fd:close()
return true
end
return persist
|