Splint fiddles.
[tools/librpm-tizen.git] / rpmio / rpmlua.c
1 /*@-bounds@*/
2 #include "system.h"
3 #include <rpmio.h>
4 #include <rpmmacro.h>
5 #include <rpmerr.h>
6 #include <rpmurl.h>
7
8 #include <lua.h>
9 #include <lualib.h>
10 #include <lauxlib.h>
11 #include <lposix.h>
12 #include <lrexlib.h>
13
14 #include <unistd.h>
15 #include <assert.h>
16
17 #define _RPMLUA_INTERNAL
18 #include "rpmlua.h"
19
20 #include "debug.h"
21
22 #if !defined(HAVE_VSNPRINTF)
23 static inline int vsnprintf(char * buf, /*@unused@*/ int nb,
24                             const char * fmt, va_list ap)
25 {
26     return vsprintf(buf, fmt, ap);
27 }
28 #endif
29
30 static int luaopen_rpm(lua_State *L)
31         /*@modifies L @*/;
32 static int rpm_print(lua_State *L)
33         /*@globals fileSystem @*/
34         /*@modifies L, fileSystem @*/;
35
36 rpmlua rpmluaNew()
37 {
38     rpmlua lua = (rpmlua) xcalloc(1, sizeof(*lua));
39     lua_State *L = lua_open();
40     /*@-readonlytrans@*/
41     /*@observer@*/ /*@unchecked@*/
42     static const luaL_reg lualibs[] = {
43         {"base", luaopen_base},
44         {"table", luaopen_table},
45         {"io", luaopen_io},
46         {"string", luaopen_string},
47         {"debug", luaopen_debug},
48         {"loadlib", luaopen_loadlib},
49         {"posix", luaopen_posix},
50         {"rex", luaopen_rex},
51         {"rpm", luaopen_rpm},
52         {NULL, NULL},
53     };
54     /*@observer@*/ /*@unchecked@*/
55     static const luaL_reg *lib = lualibs;
56     /*@=readonlytrans@*/
57
58     lua->L = L;
59     for (; lib->name; lib++) {
60         lib->func(L);
61         lua_settop(L, 0);
62     }
63     lua_pushliteral(L, "LUA_PATH");
64     lua_pushstring(L, RPMCONFIGDIR "/lua/?.lua");
65     lua_rawset(L, LUA_GLOBALSINDEX);
66     lua_pushliteral(L, "print");
67     lua_pushcfunction(L, rpm_print);
68     lua_rawset(L, LUA_GLOBALSINDEX);
69     rpmluaSetData(lua, "lua", lua);
70     return lua;
71 }
72
73 void *rpmluaFree(rpmlua lua)
74 {
75     if (lua) {
76         if (lua->L) lua_close(lua->L);
77         free(lua->printbuf);
78         free(lua);
79     }
80     return NULL;
81 }
82
83 void rpmluaSetData(rpmlua lua, const char *key, const void *data)
84 {
85     lua_State *L = lua->L;
86     lua_pushliteral(L, "rpm_");
87     lua_pushstring(L, key);
88     lua_concat(L, 2);
89     if (data == NULL)
90         lua_pushnil(L);
91     else
92         lua_pushlightuserdata(L, (void *)data);
93     lua_rawset(L, LUA_REGISTRYINDEX);
94 }
95
96 static void *getdata(lua_State *L, const char *key)
97         /*@modifies L @*/
98 {
99     void *ret = NULL;
100     lua_pushliteral(L, "rpm_");
101     lua_pushstring(L, key);
102     lua_concat(L, 2);
103     lua_rawget(L, LUA_REGISTRYINDEX);
104     if (lua_islightuserdata(L, -1))
105         ret = lua_touserdata(L, -1);
106     lua_pop(L, 1);
107     return ret;
108 }
109
110 void *rpmluaGetData(rpmlua lua, const char *key)
111 {
112     return getdata(lua->L, key);
113 }
114
115 void rpmluaSetPrintBuffer(rpmlua lua, int flag)
116 {
117     lua->storeprint = flag;
118     free(lua->printbuf);
119     lua->printbuf = NULL;
120     lua->printbufsize = 0;
121 }
122
123 const char *rpmluaGetPrintBuffer(rpmlua lua)
124 {
125     return lua->printbuf;
126 }
127
128 static int pushvar(lua_State *L, rpmluavType type, void *value)
129         /*@modifies L @*/
130 {
131     int ret = 0;
132     switch (type) {
133         case RPMLUAV_NIL:
134             lua_pushnil(L);
135             break;
136         case RPMLUAV_STRING:
137             lua_pushstring(L, *((char **)value));
138             break;
139         case RPMLUAV_NUMBER:
140             lua_pushnumber(L, *((double *)value));
141             break;
142         default:
143             ret = -1;
144             break;
145     }
146     return ret;
147 }
148
149 void rpmluaSetVar(rpmlua lua, rpmluav var)
150 {
151     lua_State *L = lua->L;
152     if (var->listmode && lua->pushsize > 0) {
153         if (var->keyType != RPMLUAV_NUMBER || var->key.num == 0) {
154             var->keyType = RPMLUAV_NUMBER;
155             var->key.num = luaL_getn(L, -1);
156         }
157         var->key.num++;
158     }
159     if (!var->listmode || lua->pushsize > 0) {
160         if (lua->pushsize == 0)
161             lua_pushvalue(L, LUA_GLOBALSINDEX);
162         if (pushvar(L, var->keyType, &var->key) != -1) {
163             if (pushvar(L, var->valueType, &var->value) != -1)
164                 lua_rawset(L, -3);
165             else
166                 lua_pop(L, 1);
167         }
168         if (lua->pushsize == 0)
169             lua_pop(L, 1);
170     }
171 }
172
173 static void popvar(lua_State *L, rpmluavType *type, void *value)
174         /*@modifies L, *type, *value @*/
175 {
176     switch (lua_type(L, -1)) {
177     case LUA_TSTRING:
178         *type = RPMLUAV_STRING;
179         *((const char **)value) = lua_tostring(L, -1);
180         break;
181     case LUA_TNUMBER:
182         *type = RPMLUAV_NUMBER;
183         *((double *)value) = lua_tonumber(L, -1);
184         break;
185     default:
186         *type = RPMLUAV_NIL;
187         *((void **)value) = NULL;
188         break;
189     }
190     lua_pop(L, 1);
191 }
192
193 void rpmluaGetVar(rpmlua lua, rpmluav var)
194 {
195     lua_State *L = lua->L;
196     if (!var->listmode) {
197         if (lua->pushsize == 0)
198             lua_pushvalue(L, LUA_GLOBALSINDEX);
199         if (pushvar(L, var->keyType, &var->key) != -1) {
200             lua_rawget(L, -2);
201             popvar(L, &var->valueType, &var->value);
202         }
203         if (lua->pushsize == 0)
204             lua_pop(L, 1);
205     } else if (lua->pushsize > 0) {
206         pushvar(L, var->keyType, &var->key);
207         if (lua_next(L, -2) != 0)
208             popvar(L, &var->valueType, &var->value);
209     }
210 }
211
212 #define FINDKEY_RETURN 0
213 #define FINDKEY_CREATE 1
214 #define FINDKEY_REMOVE 2
215 static int findkey(lua_State *L, int oper, const char *key, va_list va)
216         /*@modifies L @*/
217 {
218     char buf[BUFSIZ];
219     const char *s, *e;
220     int ret = 0;
221     vsnprintf(buf, BUFSIZ, key, va);
222     s = e = buf;
223     lua_pushvalue(L, LUA_GLOBALSINDEX);
224     for (;;) {
225         if (*e == '\0' || *e == '.') {
226             if (e != s) {
227                 lua_pushlstring(L, s, e-s);
228                 switch (oper) {
229                 case FINDKEY_REMOVE:
230                     if (*e == '\0') {
231                         lua_pushnil(L);
232                         lua_rawset(L, -3);
233                         lua_pop(L, 1);
234                         /*@switchbreak@*/ break;
235                     }
236                     /*@fallthrough@*/
237                 case FINDKEY_RETURN:
238                     lua_rawget(L, -2);
239                     lua_remove(L, -2);
240                     /*@switchbreak@*/ break;
241                 case FINDKEY_CREATE:
242                     lua_rawget(L, -2);
243                     if (!lua_istable(L, -1)) {
244                         lua_pop(L, 1);
245                         lua_newtable(L);
246                         lua_pushlstring(L, s, e-s);
247                         lua_pushvalue(L, -2);
248                         lua_rawset(L, -4);
249                     }
250                     lua_remove(L, -2);
251                     /*@switchbreak@*/ break;
252                 }
253             }
254             if (*e == '\0')
255                 break;
256             if (!lua_istable(L, -1)) {
257                 lua_pop(L, 1);
258                 ret = -1;
259                 break;
260             }
261             s = e+1;
262         }
263         e++;
264     }
265
266     return ret;
267 }
268
269 void rpmluaDelVar(rpmlua lua, const char *key, ...)
270 {
271     va_list va;
272     va_start(va, key);
273     findkey(lua->L, FINDKEY_REMOVE, key, va);
274     va_end(va);
275 }
276
277 int rpmluaVarExists(rpmlua lua, const char *key, ...)
278 {
279     int ret = 0;
280     va_list va;
281     va_start(va, key);
282     if (findkey(lua->L, FINDKEY_RETURN, key, va) == 0) {
283         if (!lua_isnil(lua->L, -1))
284             ret = 1;
285         lua_pop(lua->L, 1);
286     }
287     va_end(va);
288     return ret;
289 }
290
291 void rpmluaPushTable(rpmlua lua, const char *key, ...)
292 {
293     va_list va;
294     va_start(va, key);
295     findkey(lua->L, FINDKEY_CREATE, key, va);
296     lua->pushsize++;
297     va_end(va);
298 }
299
300 void rpmluaPop(rpmlua lua)
301 {
302     assert(lua->pushsize > 0);
303     lua->pushsize--;
304     lua_pop(lua->L, 1);
305 }
306
307 rpmluav rpmluavNew(void)
308 {
309     rpmluav var = (rpmluav) xcalloc(1, sizeof(*var));
310     return var;
311 }
312
313 void *rpmluavFree(rpmluav var)
314 {
315     free(var);
316     return NULL;
317 }
318
319 void rpmluavSetListMode(rpmluav var, int flag)
320 {
321     var->listmode = flag;
322     var->keyType = RPMLUAV_NIL;
323 }
324
325 void rpmluavSetKey(rpmluav var, rpmluavType type, const void *value)
326 {
327     var->keyType = type;
328     switch (type) {
329         case RPMLUAV_NUMBER:
330             var->key.num = *((double *)value);
331             break;
332         case RPMLUAV_STRING:
333             var->key.str = (char *)value;
334             break;
335         default:
336             break;
337     }
338 }
339
340 void rpmluavSetValue(rpmluav var, rpmluavType type, const void *value)
341 {
342     var->valueType = type;
343     switch (type) {
344         case RPMLUAV_NUMBER:
345             var->value.num = *((const double *)value);
346             break;
347         case RPMLUAV_STRING:
348             var->value.str = (const char *)value;
349             break;
350         default:
351             break;
352     }
353 }
354
355 void rpmluavGetKey(rpmluav var, rpmluavType *type, void **value)
356 {
357     *type = var->keyType;
358     switch (var->keyType) {
359         case RPMLUAV_NUMBER:
360             *((double **)value) = &var->key.num;
361             break;
362         case RPMLUAV_STRING:
363             *((const char **)value) = var->key.str;
364             break;
365         default:
366             break;
367     }
368 }
369
370 void rpmluavGetValue(rpmluav var, rpmluavType *type, void **value)
371 {
372     *type = var->valueType;
373     switch (var->valueType) {
374         case RPMLUAV_NUMBER:
375             *((double **)value) = &var->value.num;
376             break;
377         case RPMLUAV_STRING:
378             *((const char **)value) = var->value.str;
379             break;
380         default:
381             break;
382     }
383 }
384
385 void rpmluavSetKeyNum(rpmluav var, double value)
386 {
387     rpmluavSetKey(var, RPMLUAV_NUMBER, &value);
388 }
389
390 void rpmluavSetValueNum(rpmluav var, double value)
391 {
392     rpmluavSetValue(var, RPMLUAV_NUMBER, &value);
393 }
394
395 double rpmluavGetKeyNum(rpmluav var)
396 {
397     rpmluavType type;
398     void *value;
399     rpmluavGetKey(var, &type, &value);
400     if (type == RPMLUAV_NUMBER)
401         return *((double *)value);
402     return 0;
403 }
404
405 double rpmluavGetValueNum(rpmluav var)
406 {
407     rpmluavType type;
408     void *value;
409     rpmluavGetValue(var, &type, &value);
410     if (type == RPMLUAV_NUMBER)
411         return *((double *)value);
412     return 0;
413 }
414
415 int rpmluavKeyIsNum(rpmluav var)
416 {
417     return (var->keyType == RPMLUAV_NUMBER) ? 1 : 0;
418 }
419
420 int rpmluavValueIsNum(rpmluav var)
421 {
422     return (var->valueType == RPMLUAV_NUMBER) ? 1 : 0;
423 }
424
425 int rpmluaCheckScript(rpmlua lua, const char *script, const char *name)
426 {
427     lua_State *L = lua->L;
428     int ret = 0;
429     if (!name)
430         name = "<lua>";
431     if (luaL_loadbuffer(L, script, strlen(script), name) != 0) {
432         rpmError(RPMERR_SCRIPT,
433                 _("invalid syntax in lua scriptlet: %s\n"),
434                   lua_tostring(L, -1));
435         ret = -1;
436     }
437     lua_pop(L, 1); /* Error or chunk. */
438     return ret;
439 }
440
441 int rpmluaRunScript(rpmlua lua, const char *script, const char *name)
442 {
443     lua_State *L = lua->L;
444     int ret = 0;
445     if (!name)
446         name = "<lua>";
447     if (luaL_loadbuffer(L, script, strlen(script), name) != 0) {
448         rpmError(RPMERR_SCRIPT, _("invalid syntax in lua script: %s\n"),
449                  lua_tostring(L, -1));
450         lua_pop(L, 1);
451         ret = -1;
452     } else if (lua_pcall(L, 0, 0, 0) != 0) {
453         rpmError(RPMERR_SCRIPT, _("lua script failed: %s\n"),
454                  lua_tostring(L, -1));
455         lua_pop(L, 1);
456         ret = -1;
457     }
458     return ret;
459 }
460
461 /* From lua.c */
462 static int rpmluaReadline(lua_State *L, const char *prompt)
463         /*@globals fileSystem @*/
464         /*@modifies L, fileSystem @*/
465 {
466    static char buffer[1024];
467    if (prompt) {
468       fputs(prompt, stdout);
469       fflush(stdout);
470    }
471    if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
472       return 0;  /* read fails */
473    } else {
474       lua_pushstring(L, buffer);
475       return 1;
476    }
477 }
478
479 /* Based on lua.c */
480 static void _rpmluaInteractive(lua_State *L)
481         /*@globals fileSystem @*/
482         /*@modifies L, fileSystem @*/
483 {
484    fputs("\n", stdout);
485    printf("RPM Interactive %s Interpreter\n", LUA_VERSION);
486    for (;;) {
487       int rc = 0;
488
489       if (rpmluaReadline(L, "> ") == 0)
490          break;
491       if (lua_tostring(L, -1)[0] == '=') {
492          lua_pushfstring(L, "print(%s)", lua_tostring(L, -1)+1);
493          lua_remove(L, -2);
494       }
495       for (;;) {
496          rc = luaL_loadbuffer(L, lua_tostring(L, -1),
497                               lua_strlen(L, -1), "<lua>");
498          if (rc == LUA_ERRSYNTAX &&
499              strstr(lua_tostring(L, -1), "near `<eof>'") != NULL) {
500             if (rpmluaReadline(L, ">> ") == 0)
501                /*@innerbreak@*/ break;
502             lua_remove(L, -2); /* Remove error */
503             lua_concat(L, 2);
504             /*@innercontinue@*/ continue;
505          }
506          /*@innerbreak@*/ break;
507       }
508       if (rc == 0)
509          rc = lua_pcall(L, 0, 0, 0);
510       if (rc != 0) {
511          fprintf(stderr, "%s\n", lua_tostring(L, -1));
512          lua_pop(L, 1);
513       }
514       lua_pop(L, 1); /* Remove line */
515    }
516    fputs("\n", stdout);
517 }
518
519 void rpmluaInteractive(rpmlua lua)
520 {
521     _rpmluaInteractive(lua->L);
522 }
523
524 /* ------------------------------------------------------------------ */
525 /* Lua API */
526
527 static int rpm_expand(lua_State *L)
528         /*@globals rpmGlobalMacroContext, h_errno @*/
529         /*@modifies L, rpmGlobalMacroContext @*/
530 {
531     const char *str = luaL_checkstring(L, 1);
532     lua_pushstring(L, rpmExpand(str, NULL));
533     return 1;
534 }
535
536 static int rpm_define(lua_State *L)
537         /*@globals rpmGlobalMacroContext, h_errno @*/
538         /*@modifies L, rpmGlobalMacroContext @*/
539 {
540     const char *str = luaL_checkstring(L, 1);
541     rpmDefineMacro(NULL, str, 0);
542     return 0;
543 }
544
545 static int rpm_interactive(lua_State *L)
546         /*@globals fileSystem @*/
547         /*@modifies L, fileSystem @*/
548 {
549     _rpmluaInteractive(L);
550     return 0;
551 }
552
553 /* Based on luaB_print. */
554 static int rpm_print (lua_State *L)
555         /*@globals fileSystem @*/
556         /*@modifies L, fileSystem @*/
557 {
558     rpmlua lua = (rpmlua)getdata(L, "lua");
559     int n = lua_gettop(L);  /* number of arguments */
560     int i;
561     if (!lua) return 0;
562     lua_getglobal(L, "tostring");
563     for (i = 1; i <= n; i++) {
564         const char *s;
565         lua_pushvalue(L, -1);  /* function to be called */
566         lua_pushvalue(L, i);   /* value to print */
567         lua_call(L, 1, 1);
568         s = lua_tostring(L, -1);  /* get result */
569         if (s == NULL)
570             return luaL_error(L, "`tostring' must return a string to `print'");
571         if (lua->storeprint) {
572             int sl = lua_strlen(L, -1);
573             if (lua->printbufused+sl+1 > lua->printbufsize) {
574                 lua->printbufsize += sl+512;
575                 lua->printbuf = xrealloc(lua->printbuf, lua->printbufsize);
576             }
577             if (i > 1)
578                 lua->printbuf[lua->printbufused++] = '\t';
579             memcpy(lua->printbuf+lua->printbufused, s, sl+1);
580             lua->printbufused += sl;
581         } else {
582             if (i > 1)
583                 fputs("\t", stdout);
584             fputs(s, stdout);
585         }
586         lua_pop(L, 1);  /* pop result */
587     }
588     lua_pop(L, 1);
589     if (!lua->storeprint) {
590         fputs("\n", stdout);
591     } else {
592         if (lua->printbufused+1 >= lua->printbufsize) {
593             lua->printbufsize += 512;
594             lua->printbuf = xrealloc(lua->printbuf, lua->printbufsize);
595         }
596         lua->printbuf[lua->printbufused++] = '\n';
597         lua->printbuf[lua->printbufused] = '\0';
598     }
599     return 0;
600 }
601
602 /*@-readonlytrans@*/
603 /*@observer@*/ /*@unchecked@*/
604 static const luaL_reg rpmlib[] = {
605     {"expand", rpm_expand},
606     {"define", rpm_define},
607     {"interactive", rpm_interactive},
608     {NULL, NULL}
609 };
610 /*@=readonlytrans@*/
611
612 static int luaopen_rpm(lua_State *L)
613         /*@modifies L @*/
614 {
615     lua_pushvalue(L, LUA_GLOBALSINDEX);
616     luaL_openlib(L, "rpm", rpmlib, 0);
617     return 0;
618 }
619 /*@=bounds@*/