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
|
local format = string.format;
local setmetatable, type = setmetatable, type;
local pairs, ipairs = pairs, ipairs;
local char = string.char;
local loadfile, setfenv, pcall = loadfile, setfenv, pcall;
local log = log;
local io_open = io.open;
local os_remove = os.remove;
local tostring, tonumber = tostring, tonumber;
local error = error;
local next = next;
local t_insert = table.insert;
local indent = function(f, i)
for n = 1, i do
f:write("\t");
end
end
local data_path = "data";
module "datamanager"
---- utils -----
local encode, decode;
local log = function (type, msg) return log(type, "datamanager", msg); end
do
local urlcodes = setmetatable({}, { __index = function (t, k) t[k] = char(tonumber("0x"..k)); return t[k]; end });
decode = function (s)
return s and (s:gsub("+", " "):gsub("%%([a-fA-F0-9][a-fA-F0-9])", urlcodes));
end
encode = function (s)
return s and (s:gsub("%W", function (c) return format("%%%x", c:byte()); end));
end
end
local function basicSerialize (o)
if type(o) == "number" or type(o) == "boolean" then
return tostring(o);
else -- assume it is a string -- FIXME make sure it's a string. throw an error otherwise.
return (format("%q", tostring(o)):gsub("\\\n", "\\n"));
end
end
local function simplesave (f, o, ind)
if type(o) == "number" then
f:write(o)
elseif type(o) == "string" then
f:write((format("%q", o):gsub("\\\n", "\\n")))
elseif type(o) == "table" then
f:write("{\n")
for k,v in pairs(o) do
indent(f, ind);
f:write("[", basicSerialize(k), "] = ")
simplesave(f, v, ind+1)
f:write(",\n")
end
indent(f, ind-1);
f:write("}")
elseif type(o) == "boolean" then
f:write(o and "true" or "false");
else
error("cannot serialize a " .. type(o))
end
end
------- API -------------
function set_data_path(path)
data_path = path;
end
function getpath(username, host, datastore, ext)
ext = ext or "dat";
if username then
return format("%s/%s/%s/%s.%s", data_path, encode(host), datastore, encode(username), ext);
elseif host then
return format("%s/%s/%s.%s", data_path, encode(host), datastore, ext);
else
return format("%s/%s.%s", data_path, datastore, ext);
end
end
function load(username, host, datastore)
local data, ret = loadfile(getpath(username, host, datastore));
if not data then
log("warn", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
return nil;
end
setfenv(data, {});
local success, ret = pcall(data);
if not success then
log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
return nil;
end
return ret;
end
function store(username, host, datastore, data)
if not data then
data = {};
end
-- save the datastore
local f, msg = io_open(getpath(username, host, datastore), "w+");
if not f then
log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
return;
end
f:write("return ");
simplesave(f, data, 1);
f:close();
if not next(data) then -- try to delete empty datastore
os_remove(getpath(username, host, datastore));
end
-- we write data even when we are deleting because lua doesn't have a
-- platform independent way of checking for non-exisitng files
return true;
end
function list_append(username, host, datastore, data)
if not data then return; end
-- save the datastore
local f, msg = io_open(getpath(username, host, datastore, "list"), "a+");
if not f then
log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
return;
end
f:write("item(");
simplesave(f, data, 1);
f:write(");\n");
f:close();
return true;
end
function list_store(username, host, datastore, data)
if not data then
data = {};
end
-- save the datastore
local f, msg = io_open(getpath(username, host, datastore, "list"), "w+");
if not f then
log("error", "Unable to write to "..datastore.." storage ('"..msg.."') for user: "..(username or "nil").."@"..(host or "nil"));
return;
end
for _, d in ipairs(data) do
f:write("item(");
simplesave(f, d, 1);
f:write(");\n");
end
f:close();
if not next(data) then -- try to delete empty datastore
os_remove(getpath(username, host, datastore, "list"));
end
-- we write data even when we are deleting because lua doesn't have a
-- platform independent way of checking for non-exisitng files
return true;
end
function list_load(username, host, datastore)
local data, ret = loadfile(getpath(username, host, datastore, "list"));
if not data then
log("warn", "Failed to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
return nil;
end
local items = {};
setfenv(data, {item = function(i) t_insert(items, i); end});
local success, ret = pcall(data);
if not success then
log("error", "Unable to load "..datastore.." storage ('"..ret.."') for user: "..(username or "nil").."@"..(host or "nil"));
return nil;
end
return items;
end
return _M;
|