applications/luci-upnp: add a note that up and download speeds are informational...
[project/luci.git] / contrib / lar / larlib.c
1 /*
2  * lar - Lua Archive Library
3  *
4  *   Copyright (C) 2009 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
20 #include "lua.h"
21 #include "lualib.h"
22 #include "lauxlib.h"
23 #include "lar.h"
24
25 typedef struct {
26         int fd;
27         char *data;
28         size_t length;
29 } mmap_handle;
30
31 static int larlib_perror( lua_State *L, const char *message )
32 {
33         lua_pushnil(L);
34         lua_pushstring(L, message);
35
36         return 2;
37 }
38
39 int larlib_open( lua_State *L )
40 {
41         lar_archive *ar, **udata;
42         const char *filename = luaL_checkstring( L, 1 );
43
44         if( filename != NULL && (ar = lar_open(filename)) != NULL )
45         {
46                 if( (udata = lua_newuserdata(L, sizeof(lar_archive *))) != NULL )
47                 {
48                         *udata = ar;
49                         luaL_getmetatable(L, "lar.archive");
50                         lua_setmetatable(L, -2);
51                 }
52                 else
53                 {
54                         return luaL_error(L, "Out of memory");
55                 }
56         }
57         else
58         {
59                 return larlib_perror(L, "Archive not found");
60         }
61
62         return 1;
63 }
64
65 int larlib_find( lua_State *L )
66 {
67         const char *filename = luaL_checkstring( L, 1 );
68         const char *basepath = luaL_optstring( L, 2, "./" );
69         int is_pkg = strstr(filename, "/") ? 0 : 1;
70         lar_archive *ar, **udata;
71
72         if( ((ar = lar_find_archive(filename, basepath, is_pkg)) != NULL) ||
73             ((ar = lar_find_archive(filename, LUA_LDIR, is_pkg)) != NULL) ||
74                 ((ar = lar_find_archive(filename, LUA_CDIR, is_pkg)) != NULL) )
75         {
76                 if( (udata = lua_newuserdata(L, sizeof(lar_archive *))) != NULL )
77                 {
78                         *udata = ar;
79                         luaL_getmetatable(L, "lar.archive");
80                         lua_setmetatable(L, -2);
81                 }
82                 else
83                 {
84                         return luaL_error(L, "Out of memory");
85                 }
86         }
87         else
88         {
89                 return larlib_perror(L, "Archive not found");
90         }
91
92         return 1;
93 }
94
95 int larlib_md5( lua_State *L )
96 {
97         int i;
98         char md5[16], md5_hex[33];
99         const char *data = luaL_checkstring( L, 1 );
100         md5_state_t state;
101
102         md5_init(&state);
103         md5_append(&state, (const md5_byte_t *)data, strlen(data));
104         md5_finish(&state, (md5_byte_t *)md5);
105
106         for( i = 0; i < 16; i++ )
107                 sprintf(&md5_hex[i*2], "%02x", (unsigned char)md5[i]);
108
109         lua_pushstring(L, md5_hex);
110         return 1;
111 }
112
113 int larlib_md5_file( lua_State *L )
114 {
115         int i, fd, len;
116         char md5[16], md5_hex[33], buffer[1024];
117         const char *filename = luaL_checkstring( L, 1 );
118         md5_state_t state;
119
120         if( (fd = open(filename, O_RDONLY)) != -1 )
121         {
122                 md5_init(&state);
123
124                 while( (len = read(fd, buffer, 1024)) > 0 )
125                         md5_append(&state, (const md5_byte_t *)buffer, len);
126
127                 md5_finish(&state, (md5_byte_t *)md5);
128
129                 for( i = 0; i < 16; i++ )
130                         sprintf(&md5_hex[i*2], "%02x", (unsigned char)md5[i]);
131
132                 close(fd);
133                 lua_pushstring(L, md5_hex);
134         }
135         else
136         {
137                 return larlib_perror(L, strerror(errno));
138         }
139
140         return 1;
141 }
142
143 static int larlib_mkpath( const char *name, const char *path, char *buffer )
144 {
145         int nlen = strlen(name);
146         int plen = strlen(path);
147
148         if( (nlen + plen + 1) <= LAR_FNAME_BUFFER )
149         {
150                 strcpy(buffer, path);
151
152                 if( buffer[plen-1] != '/' )
153                         buffer[plen++] = '/';
154
155                 strcpy(&buffer[plen], name);
156                 buffer[plen + nlen] = '\0';
157
158                 return 0;
159         }
160
161         return 1;
162 }
163
164 static int larlib__gc( lua_State *L )
165 {
166         lar_archive **archive = luaL_checkudata( L, 1, "lar.archive" );
167
168         if( *archive )
169                 lar_close(*archive);
170
171         *archive = NULL;
172         return 0;
173 }
174
175
176 static int larlib_member__open( lua_State *L, lar_member *mb )
177 {
178         lar_archive **archive = NULL;
179         const char *filename = NULL;
180         lar_member **udata;
181
182         if( mb == NULL )
183         {
184                 *archive = luaL_checkudata( L, 1, "lar.archive" );
185                 filename = luaL_checkstring( L, 2 );
186         }
187
188         if( mb != NULL || (mb = lar_open_member(*archive, filename)) != NULL )
189         {
190                 if( (udata = lua_newuserdata(L, sizeof(lar_member *))) != NULL )
191                 {
192                         *udata = mb;
193                         luaL_getmetatable(L, "lar.member");
194                         lua_setmetatable(L, -2);
195                 }
196                 else
197                 {
198                         return luaL_error(L, "Out of memory");
199                 }
200         }
201         else
202         {
203                 return larlib_perror(L, "Member not found in archive");
204         }
205
206         return 1;
207 }
208
209 int larlib_member_open( lua_State *L )
210 {
211         return larlib_member__open( L, NULL );
212 }
213
214 int larlib_member_find( lua_State *L )
215 {
216         lar_archive **archive = luaL_checkudata( L, 1, "lar.archive" );
217         const char *package = luaL_checkstring( L, 2 );
218         lar_member *mb, **udata;
219
220         if( (mb = lar_find_member(*archive, package)) != NULL )
221         {
222                 if( (udata = lua_newuserdata(L, sizeof(lar_member *))) != NULL )
223                 {
224                         *udata = mb;
225                         luaL_getmetatable(L, "lar.member");
226                         lua_setmetatable(L, -2);
227                 }
228                 else
229                 {
230                         return luaL_error(L, "Out of memory");
231                 }
232         }
233         else
234         {
235                 return larlib_perror(L, "Member not found in archive");
236         }
237
238         return 1;
239 }
240
241 int larlib_member_size( lua_State *L )
242 {
243         lar_member **member = luaL_checkudata( L, 1, "lar.member" );
244         lua_pushnumber(L, (*member)->length);
245         return 1;
246 }
247
248 int larlib_member_type( lua_State *L )
249 {
250         lar_member **member = luaL_checkudata( L, 1, "lar.member" );
251         lua_pushnumber(L, (*member)->type);
252         return 1;
253 }
254
255 int larlib_member_flags( lua_State *L )
256 {
257         lar_member **member = luaL_checkudata( L, 1, "lar.member" );
258         lua_pushnumber(L, (*member)->flags);
259         return 1;
260 }
261
262 int larlib_member_read( lua_State *L )
263 {
264         lar_member **member = luaL_checkudata( L, 1, "lar.member" );
265         int start  = luaL_checknumber( L, 2 );
266         int length = luaL_optnumber( L, 3, (*member)->length );
267         char *stringcopy;
268
269         if( (start >= 0) && (start < (*member)->length) && (length > 0) )
270         {
271                 if( (start + length) >= (*member)->length )
272                         length = (*member)->length - start;
273
274                 if( (stringcopy = (char *)malloc(length + 1)) != NULL )
275                 {
276                         memcpy(stringcopy, &(*member)->data[start], length);
277                         stringcopy[length] = '\0';
278                         lua_pushstring(L, stringcopy);
279                         free(stringcopy);
280                 }
281                 else
282                 {
283                         return luaL_error(L, "Out of memory");
284                 }
285         }
286         else
287         {
288                 return larlib_perror(L, "Invalid argument");
289         }
290
291         return 1;
292 }
293
294 int larlib_member_data( lua_State *L )
295 {
296         lar_member **member = luaL_checkudata( L, 1, "lar.member" );
297         lua_pushstring(L, (*member)->data);
298         return 1;
299 }
300
301 int larlib_member_load( lua_State *L )
302 {
303         lar_member **member = luaL_checkudata( L, 1, "lar.member" );
304         int status = luaL_loadbuffer( L, (*member)->data, (*member)->length,
305                 "=(lar member)" );
306
307         if( status )
308         {
309                 lua_pushnil(L);
310                 lua_insert(L, -2);
311                 return 2;
312         }
313
314         return 1;
315 }
316
317 static int larlib_member__gc( lua_State *L )
318 {
319         lar_member **member = luaL_checkudata( L, 1, "lar.member" );
320
321         if( *member )
322                 lar_close_member(*member);
323
324         *member = NULL;
325         return 0;
326 }
327
328
329 static int larlib_mmfile__open( lua_State *L, const char *filename )
330 {
331         struct stat s;
332         mmap_handle *fh, **udata;
333
334         if( filename == NULL )
335                 filename = (const char *)luaL_checkstring( L, 1 );
336
337         if( (fh = (mmap_handle *)malloc(sizeof(mmap_handle))) == NULL )
338                 return larlib_perror(L, "Out of memory");
339
340         if( stat(filename, &s) > -1 && (fh->fd = open(filename, O_RDONLY)) > -1 )
341         {
342                 fh->length = s.st_size;
343                 fh->data   = mmap( 0, s.st_size, PROT_READ, MAP_PRIVATE, fh->fd, 0 );
344
345                 if( fh->data == MAP_FAILED )
346                         return larlib_perror(L, "Failed to mmap() file");
347
348                 if( (udata = lua_newuserdata(L, sizeof(char *))) != NULL )
349                 {
350                         *udata = fh;
351                         luaL_getmetatable(L, "lar.mmfile");
352                         lua_setmetatable(L, -2);
353                 }
354                 else
355                 {
356                         return larlib_perror(L, "Out of memory");
357                 }
358         }
359         else
360         {
361                 return larlib_perror(L, strerror(errno));
362         }
363
364         return 1;
365 }
366
367 int larlib_mmfile_open( lua_State *L )
368 {
369         return larlib_mmfile__open(L, NULL);
370 }
371
372 int larlib_mmfile_size( lua_State *L )
373 {
374         mmap_handle **fh = luaL_checkudata( L, 1, "lar.mmfile" );
375         lua_pushnumber(L, (*fh)->length);
376         return 1;
377 }
378
379 int larlib_mmfile_read( lua_State *L )
380 {
381         mmap_handle **fh = luaL_checkudata( L, 1, "lar.mmfile" );
382         int start  = luaL_checknumber( L, 2 );
383         int length = luaL_optnumber( L, 3, (*fh)->length );
384         char *stringcopy;
385
386         if( (start >= 0) && (start < (*fh)->length) && (length > 0) )
387         {
388                 if( (start + length) >= (*fh)->length )
389                         length = (*fh)->length - start;
390
391                 if( (stringcopy = (char *)malloc(length + 1)) != NULL )
392                 {
393                         memcpy(stringcopy, &(*fh)->data[start], length);
394                         stringcopy[length] = '\0';
395                         lua_pushstring(L, stringcopy);
396                         free(stringcopy);
397                 }
398                 else
399                 {
400                         return luaL_error(L, "Out of memory");
401                 }
402         }
403         else
404         {
405                 return larlib_perror(L, "Invalid argument");
406         }
407
408         return 1;
409 }
410
411 int larlib_mmfile_data( lua_State *L )
412 {
413         mmap_handle **fh = luaL_checkudata( L, 1, "lar.mmfile" );
414         lua_pushstring(L, (*fh)->data);
415         return 1;
416 }
417
418 int larlib_mmfile_load( lua_State *L )
419 {
420         mmap_handle **fh = luaL_checkudata( L, 1, "lar.mmfile" );
421         int status = luaL_loadbuffer(L, (*fh)->data, (*fh)->length, "=(mmap file)");
422
423         if( status )
424         {
425                 lua_pushnil(L);
426                 lua_insert(L, -2);
427                 return 2;
428         }
429
430         return 1;
431 }
432
433 static int larlib_mmfile__gc( lua_State *L )
434 {
435         mmap_handle **fh = luaL_checkudata( L, 1, "lar.mmfile" );
436
437         if( *fh )
438         {
439                 close((*fh)->fd);
440                 munmap((*fh)->data, (*fh)->length);
441                 free(*fh);
442                 *fh = NULL;
443         }
444
445         return 0;
446 }
447
448
449 int larlib_findfile( lua_State *L )
450 {
451         int i;
452         const char *filename = luaL_checkstring( L, 1 );
453         const char *basepath = luaL_optstring( L, 2, "./" );
454         struct stat s;
455         lar_archive *ar;
456         lar_member  *mb;
457         LAR_FNAME(filepath);
458
459         const char *searchpath[3] = { basepath, LUA_LDIR, LUA_CDIR };
460
461         for( i = 0; i < 3; i++ )
462                 if( !larlib_mkpath(filename, searchpath[i], filepath) )
463                         if( stat(filepath, &s) > -1 && (s.st_mode & S_IFREG) )
464                                 return larlib_mmfile__open( L, filepath );
465
466         for( i = 0; i < 3; i++ )
467                 if( (ar = lar_find_archive(filename, searchpath[i], 0)) != NULL )
468                         if( (mb = lar_open_member(ar, filename)) != NULL )
469                                 return larlib_member__open( L, mb );
470
471         return larlib_perror(L, "File not found");
472 }
473
474
475 static const luaL_reg LAR_REG[] = {
476         { "open",                       larlib_open             },
477         { "find",                       larlib_find             },
478         { "md5",                        larlib_md5                      },
479         { "md5_file",           larlib_md5_file         },
480         { "mmap",                       larlib_mmfile_open      },
481         { "findfile",           larlib_findfile         },
482         { NULL,                         NULL                            }
483 };
484
485 static const luaL_reg LAR_ARCHIVE_REG[] = {
486         { "member",                     larlib_member_open      },
487         { "find",                       larlib_member_find      },
488         { "__gc",                       larlib__gc                      },
489         { NULL,                         NULL                            }
490 };
491
492 static const luaL_reg LAR_MEMBER_REG[] = {
493         { "size",                       larlib_member_size      },
494         { "type",                       larlib_member_type      },
495         { "flags",                      larlib_member_flags     },
496         { "read",                       larlib_member_read      },
497         { "data",                       larlib_member_data      },
498         { "load",                       larlib_member_load      },
499         { "__gc",                       larlib_member__gc       },
500         { NULL,                         NULL                            }
501 };
502
503 static const luaL_reg LAR_MMFILE_REG[] = {
504         { "size",                       larlib_mmfile_size      },
505         { "read",                       larlib_mmfile_read      },
506         { "data",                       larlib_mmfile_data      },
507         { "load",                       larlib_mmfile_load      },
508         { "__gc",                       larlib_mmfile__gc       },
509         { NULL,                         NULL                            }
510 };
511
512
513 LUALIB_API int luaopen_larlib( lua_State *L )
514 {
515         luaL_newmetatable(L, "lar");
516         luaL_register(L, NULL, LAR_REG);
517         lua_pushvalue(L, -1);
518         lua_setfield(L, -2, "__index");
519         lua_setglobal(L, "lar");
520
521         luaL_newmetatable(L, "lar.archive");
522         luaL_register(L, NULL, LAR_ARCHIVE_REG);
523         lua_pushvalue(L, -1);
524         lua_setfield(L, -2, "__index");
525         lua_setglobal(L, "lar.archive");
526
527         luaL_newmetatable(L, "lar.member");
528         luaL_register(L, NULL, LAR_MEMBER_REG);
529         lua_pushvalue(L, -1);
530         lua_setfield(L, -2, "__index");
531         lua_setglobal(L, "lar.member");
532
533         luaL_newmetatable(L, "lar.mmfile");
534         luaL_register(L, NULL, LAR_MMFILE_REG);
535         lua_pushvalue(L, -1);
536         lua_setfield(L, -2, "__index");
537         lua_setglobal(L, "lar.mmfile");
538
539         return 1;
540 }