5cc1e7d69ddaa58c97d40a20b0c06f0afd488028
[project/luci.git] / libs / lmo / src / lmo_lualib.c
1 /*
2  * lmo - Lua Machine Objects - Lua binding
3  *
4  *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "lmo_lualib.h"
20
21 extern char _lmo_error[1024];
22
23
24 static int lmo_L_open(lua_State *L) {
25         const char *filename = luaL_checklstring(L, 1, NULL);
26         lmo_archive_t *ar, **udata;
27
28         if( (ar = lmo_open(filename)) != NULL )
29         {
30                 if( (udata = lua_newuserdata(L, sizeof(lmo_archive_t *))) != NULL )
31                 {
32                         *udata = ar;
33                         luaL_getmetatable(L, LMO_ARCHIVE_META);
34                         lua_setmetatable(L, -2);
35                         return 1;
36                 }
37
38                 lmo_close(ar);
39                 lua_pushnil(L);
40                 lua_pushstring(L, "out of memory");
41                 return 2;
42         }
43
44         lua_pushnil(L);
45         lua_pushstring(L, lmo_error());
46         return 2;
47 }
48
49 static uint32_t _lmo_hash_string(lua_State *L, int n) {
50         size_t len;
51         const char *str = luaL_checklstring(L, n, &len);
52         char res[4096];
53         char *ptr, prev;
54
55         if (!str || len >= sizeof(res))
56                 return 0;
57
58         for (prev = ' ', ptr = res; *str; prev = *str, str++)
59         {
60                 if (isspace(*str))
61                 {
62                         if (!isspace(prev))
63                                 *ptr++ = ' ';
64                 }
65                 else
66                 {
67                         *ptr++ = *str;
68                 }
69         }
70
71         if ((ptr > res) && isspace(*(ptr-1)))
72                 ptr--;
73
74         return sfh_hash(res, ptr - res);
75 }
76
77 static int lmo_L_hash(lua_State *L) {
78         uint32_t hash = _lmo_hash_string(L, 1);
79         lua_pushinteger(L, (lua_Integer)hash);
80         return 1;
81 }
82
83 static lmo_luaentry_t *_lmo_push_entry(lua_State *L) {
84         lmo_luaentry_t *le;
85
86         if( (le = lua_newuserdata(L, sizeof(lmo_luaentry_t))) != NULL )
87         {
88                 luaL_getmetatable(L, LMO_ENTRY_META);
89                 lua_setmetatable(L, -2);
90
91                 return le;
92         }
93
94         return NULL;
95 }
96
97 static int _lmo_lookup(lua_State *L, lmo_archive_t *ar, uint32_t hash) {
98         lmo_entry_t *e = ar->index;
99         lmo_luaentry_t *le = NULL;
100
101         while( e != NULL )
102         {
103                 if( e->key_id == hash )
104                 {
105                         if( (le = _lmo_push_entry(L)) != NULL )
106                         {
107                                 le->archive = ar;
108                                 le->entry   = e;
109                                 return 1;
110                         }
111                         else
112                         {
113                                 lua_pushnil(L);
114                                 lua_pushstring(L, "out of memory");
115                                 return 2;
116                         }
117                 }
118
119                 e = e->next;
120         }
121
122         lua_pushnil(L);
123         return 1;
124 }
125
126 static int lmo_L_get(lua_State *L) {
127         lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
128         uint32_t hash = (uint32_t) luaL_checkinteger(L, 2);
129         return _lmo_lookup(L, *ar, hash);
130 }
131
132 static int lmo_L_lookup(lua_State *L) {
133         lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
134         uint32_t hash = _lmo_hash_string(L, 2);
135         return _lmo_lookup(L, *ar, hash);
136 }
137
138 static int lmo_L_foreach(lua_State *L) {
139         lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
140         lmo_entry_t *e = (*ar)->index;
141
142         if( lua_isfunction(L, 2) )
143         {
144                 while( e != NULL )
145                 {
146                         lua_pushvalue(L, 2);
147                         lua_pushinteger(L, e->key_id);
148                         lua_pushlstring(L, &(*ar)->mmap[e->offset], e->length);
149                         lua_pcall(L, 2, 0, 0);
150                         e = e->next;
151                 }
152         }
153
154         return 0;
155 }
156
157 static int lmo_L__gc(lua_State *L) {
158         lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
159
160         if( (*ar) != NULL )
161                 lmo_close(*ar);
162
163         *ar = NULL;
164
165         return 0;
166 }
167
168 static int lmo_L__tostring(lua_State *L) {
169         lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
170         lua_pushfstring(L, "LMO Archive (%d bytes)", (*ar)->length);
171         return 1;
172 }
173
174
175 static int _lmo_convert_entry(lua_State *L, int idx) {
176         lmo_luaentry_t *le = luaL_checkudata(L, idx, LMO_ENTRY_META);
177
178         lua_pushlstring(L,
179                 &le->archive->mmap[le->entry->offset],
180                 le->entry->length
181         );
182
183         return 1;
184 }
185
186 static int lmo_L_entry__tostring(lua_State *L) {
187         return _lmo_convert_entry(L, 1);
188 }
189
190 static int lmo_L_entry__concat(lua_State *L) {
191         if( lua_isuserdata(L, 1) )
192                 _lmo_convert_entry(L, 1);
193         else
194                 lua_pushstring(L, lua_tostring(L, 1));
195
196         if( lua_isuserdata(L, 2) )
197                 _lmo_convert_entry(L, 2);
198         else
199                 lua_pushstring(L, lua_tostring(L, 2));
200
201         lua_concat(L, 2);
202
203         return 1;
204 }
205
206 static int lmo_L_entry__len(lua_State *L) {
207         lmo_luaentry_t *le = luaL_checkudata(L, 1, LMO_ENTRY_META);
208         lua_pushinteger(L, le->entry->length);
209         return 1;
210 }
211
212 static int lmo_L_entry__gc(lua_State *L) {
213         lmo_luaentry_t *le = luaL_checkudata(L, 1, LMO_ENTRY_META);
214         le->archive = NULL;
215         le->entry   = NULL;
216         return 0;
217 }
218
219
220 /* lmo method table */
221 static const luaL_reg M[] = {
222         {"close",               lmo_L__gc},
223         {"get",                 lmo_L_get},
224         {"lookup",              lmo_L_lookup},
225         {"foreach",             lmo_L_foreach},
226         {"__tostring",  lmo_L__tostring},
227         {"__gc",                lmo_L__gc},
228         {NULL,                  NULL}
229 };
230
231 /* lmo.entry method table */
232 static const luaL_reg E[] = {
233         {"__tostring",  lmo_L_entry__tostring},
234         {"__concat",    lmo_L_entry__concat},
235         {"__len",               lmo_L_entry__len},
236         {"__gc",                lmo_L_entry__gc},
237         {NULL,                  NULL}
238 };
239
240 /* module table */
241 static const luaL_reg R[] = {
242         {"open",        lmo_L_open},
243         {"hash",        lmo_L_hash},
244         {NULL,          NULL}
245 };
246
247 LUALIB_API int luaopen_lmo(lua_State *L) {
248         luaL_newmetatable(L, LMO_ARCHIVE_META);
249         luaL_register(L, NULL, M);
250         lua_pushvalue(L, -1);
251         lua_setfield(L, -2, "__index");
252         lua_setglobal(L, LMO_ARCHIVE_META);
253
254         luaL_newmetatable(L, LMO_ENTRY_META);
255         luaL_register(L, NULL, E);
256         lua_pushvalue(L, -1);
257         lua_setfield(L, -2, "__index");
258         lua_setglobal(L, LMO_ENTRY_META);       
259
260         luaL_register(L, LMO_LUALIB_META, R);
261
262         return 1;
263 }