Add librpm-tizen.spec file & Debian packaging.
[tools/librpm-tizen.git] / rpmio / rpmlua.c
1 #include "system.h"
2
3 #ifdef  WITH_LUA
4 #include <lua.h>
5 #include <lualib.h>
6 #include <lauxlib.h>
7 #include <lposix.h>
8 #include <lrexlib.h>
9
10 /* replaced in 5.1 */
11 #ifndef lua_open
12 #define lua_open()      luaL_newstate()
13 #endif
14
15 /* defined as lua_objlen in 5.1 */
16 #ifndef lua_strlen
17 #define lua_strlen(L,i) lua_rawlen(L, (i))
18 #endif
19
20 /* deprecated in 5.1, defined as lua_objlen in 5.1 */
21 #ifndef luaL_getn
22 #define luaL_getn(L,i)  ((int)lua_rawlen(L, i))
23 #endif
24
25 /* define added in 5.2 */
26 #ifndef lua_pushglobaltable
27 #define lua_pushglobaltable(L)  lua_pushvalue(L, LUA_GLOBALSINDEX)
28 #endif
29
30 #include <unistd.h>
31 #include <assert.h>
32
33 #include <rpm/rpmio.h>
34 #include <rpm/rpmmacro.h>
35 #include <rpm/rpmlog.h>
36 #include <rpm/rpmurl.h>
37 #include <rpm/rpmfileutil.h>
38 #include <rpm/rpmbase64.h>
39 #include "rpmio/rpmhook.h"
40
41 #define _RPMLUA_INTERNAL
42 #include "rpmio/rpmlua.h"
43
44 #include "debug.h"
45
46 #define INITSTATE(_lua, lua) \
47     rpmlua lua = _lua ? _lua : \
48             (globalLuaState ? globalLuaState : \
49                         \
50                         (globalLuaState = rpmluaNew()) \
51                         \
52             )
53
54 struct rpmluapb_s {
55     size_t alloced;
56     size_t used;
57     char *buf;
58     rpmluapb next;
59 };
60
61 static rpmlua globalLuaState = NULL;
62
63 static char *(*nextFileFunc)(void *) = NULL;
64 static void *nextFileFuncParam = NULL;
65
66 static int luaopen_rpm(lua_State *L);
67 static int rpm_print(lua_State *L);
68
69 rpmlua rpmluaGetGlobalState(void)
70 {
71     INITSTATE(NULL, lua);
72     return lua;
73 }
74
75 rpmlua rpmluaNew()
76 {
77     rpmlua lua = (rpmlua) xcalloc(1, sizeof(*lua));
78     struct stat st;
79     const luaL_Reg *lib;
80     char *initlua = rpmGenPath(rpmConfigDir(), "init.lua", NULL);
81    
82     static const luaL_Reg extlibs[] = {
83         {"posix", luaopen_posix},
84         {"rex", luaopen_rex},
85         {"rpm", luaopen_rpm},
86         {"os",  luaopen_rpm_os},
87         {NULL, NULL},
88     };
89     
90     lua_State *L = lua_open();
91     luaL_openlibs(L);
92     lua->L = L;
93
94     for (lib = extlibs; lib->name; lib++) {
95         lua_pushcfunction(L, lib->func);
96         lua_pushstring(L, lib->name);
97         lua_call(L, 1, 0);
98         lua_settop(L, 0);
99     }
100 #ifndef LUA_GLOBALSINDEX
101     lua_pushglobaltable(L);
102 #endif
103     lua_pushliteral(L, "LUA_PATH");
104     lua_pushfstring(L, "%s/%s", rpmConfigDir(), "/lua/?.lua");
105 #ifdef LUA_GLOBALSINDEX
106     lua_rawset(L, LUA_GLOBALSINDEX);
107 #else
108     lua_settable(L, -3);
109 #endif
110     lua_pushliteral(L, "print");
111     lua_pushcfunction(L, rpm_print);
112 #ifdef LUA_GLOBALSINDEX
113     lua_rawset(L, LUA_GLOBALSINDEX);
114 #else
115     lua_settable(L, -3);
116 #endif
117 #ifndef LUA_GLOBALSINDEX
118     lua_pop(L, 1);
119 #endif
120     rpmluaSetData(lua, "lua", lua);
121     if (stat(initlua, &st) != -1)
122         (void)rpmluaRunScriptFile(lua, initlua);
123     free(initlua);
124     return lua;
125 }
126
127 rpmlua rpmluaFree(rpmlua lua)
128 {
129     if (lua) {
130         if (lua->L) lua_close(lua->L);
131         free(lua->printbuf);
132         free(lua);
133         if (lua == globalLuaState) globalLuaState = NULL;
134     }
135     return NULL;
136 }
137
138 void rpmluaSetData(rpmlua _lua, const char *key, const void *data)
139 {
140     INITSTATE(_lua, lua);
141     lua_State *L = lua->L;
142     lua_pushliteral(L, "rpm_");
143     lua_pushstring(L, key);
144     lua_concat(L, 2);
145     if (data == NULL)
146         lua_pushnil(L);
147     else
148         lua_pushlightuserdata(L, (void *)data);
149     lua_rawset(L, LUA_REGISTRYINDEX);
150 }
151
152 static void *getdata(lua_State *L, const char *key)
153 {
154     void *ret = NULL;
155     lua_pushliteral(L, "rpm_");
156     lua_pushstring(L, key);
157     lua_concat(L, 2);
158     lua_rawget(L, LUA_REGISTRYINDEX);
159     if (lua_islightuserdata(L, -1))
160         ret = lua_touserdata(L, -1);
161     lua_pop(L, 1);
162     return ret;
163 }
164
165 void *rpmluaGetData(rpmlua _lua, const char *key)
166 {
167     INITSTATE(_lua, lua);
168     return getdata(lua->L, key);
169 }
170
171 void rpmluaPushPrintBuffer(rpmlua _lua)
172 {
173     INITSTATE(_lua, lua);
174     rpmluapb prbuf = xcalloc(1, sizeof(*prbuf));
175     prbuf->buf = NULL;
176     prbuf->alloced = 0;
177     prbuf->used = 0;
178     prbuf->next = lua->printbuf;
179
180     lua->printbuf = prbuf;
181 }
182
183 char *rpmluaPopPrintBuffer(rpmlua _lua)
184 {
185     INITSTATE(_lua, lua);
186     rpmluapb prbuf = lua->printbuf;
187     char *ret = NULL;
188
189     if (prbuf) {
190         ret = prbuf->buf;
191         lua->printbuf = prbuf->next;
192         free(prbuf);
193     }
194     
195     return ret;
196 }
197
198 static int pushvar(lua_State *L, rpmluavType type, void *value)
199 {
200     int ret = 0;
201     switch (type) {
202         case RPMLUAV_NIL:
203             lua_pushnil(L);
204             break;
205         case RPMLUAV_STRING:
206             lua_pushstring(L, *((char **)value));
207             break;
208         case RPMLUAV_NUMBER:
209             lua_pushnumber(L, *((double *)value));
210             break;
211         default:
212             ret = -1;
213             break;
214     }
215     return ret;
216 }
217
218 void rpmluaSetNextFileFunc(char *(*func)(void *), void *funcParam)
219 {
220     nextFileFunc = func;
221     nextFileFuncParam = funcParam;
222 }
223
224 void rpmluaSetVar(rpmlua _lua, rpmluav var)
225 {
226     INITSTATE(_lua, lua);
227     lua_State *L = lua->L;
228     if (var->listmode && lua->pushsize > 0) {
229         if (var->keyType != RPMLUAV_NUMBER || var->key.num == (double)0) {
230             var->keyType = RPMLUAV_NUMBER;
231             var->key.num = (double) luaL_getn(L, -1);
232         }
233         var->key.num++;
234     }
235     if (!var->listmode || lua->pushsize > 0) {
236         if (lua->pushsize == 0)
237             lua_pushglobaltable(L);
238         if (pushvar(L, var->keyType, &var->key) != -1) {
239             if (pushvar(L, var->valueType, &var->value) != -1)
240                 lua_rawset(L, -3);
241             else
242                 lua_pop(L, 1);
243         }
244         if (lua->pushsize == 0)
245             lua_pop(L, 1);
246     }
247 }
248
249 static void popvar(lua_State *L, rpmluavType *type, void *value)
250 {
251     switch (lua_type(L, -1)) {
252     case LUA_TSTRING:
253         *type = RPMLUAV_STRING;
254         *((const char **)value) = lua_tostring(L, -1);
255         break;
256     case LUA_TNUMBER:
257         *type = RPMLUAV_NUMBER;
258         *((double *)value) = lua_tonumber(L, -1);
259         break;
260     default:
261         *type = RPMLUAV_NIL;
262         *((void **)value) = NULL;
263         break;
264     }
265     lua_pop(L, 1);
266 }
267
268 void rpmluaGetVar(rpmlua _lua, rpmluav var)
269 {
270     INITSTATE(_lua, lua);
271     lua_State *L = lua->L;
272     if (!var->listmode) {
273         if (lua->pushsize == 0)
274             lua_pushglobaltable(L);
275         if (pushvar(L, var->keyType, &var->key) != -1) {
276             lua_rawget(L, -2);
277             popvar(L, &var->valueType, &var->value);
278         }
279         if (lua->pushsize == 0)
280             lua_pop(L, 1);
281     } else if (lua->pushsize > 0) {
282         (void) pushvar(L, var->keyType, &var->key);
283         if (lua_next(L, -2) != 0)
284             popvar(L, &var->valueType, &var->value);
285     }
286 }
287
288 #define FINDKEY_RETURN 0
289 #define FINDKEY_CREATE 1
290 #define FINDKEY_REMOVE 2
291 static int findkey(lua_State *L, int oper, const char *key, va_list va)
292 {
293     char *buf;
294     const char *s, *e;
295     int ret = 0;
296     int blen;
297
298     blen = vsnprintf(NULL, 0, key, va);
299     if (blen <= 0) {
300         return -1;
301     }
302
303     buf = xmalloc(blen + 1);
304     vsnprintf(buf, blen + 1, key, va);
305
306     s = e = buf;
307     lua_pushglobaltable(L);
308     for (;;) {
309         if (*e == '\0' || *e == '.') {
310             if (e != s) {
311                 lua_pushlstring(L, s, e-s);
312                 switch (oper) {
313                 case FINDKEY_REMOVE:
314                     if (*e == '\0') {
315                         lua_pushnil(L);
316                         lua_rawset(L, -3);
317                         lua_pop(L, 1);
318                         break;
319                     }
320                 case FINDKEY_RETURN:
321                     lua_rawget(L, -2);
322                     lua_remove(L, -2);
323                     break;
324                 case FINDKEY_CREATE:
325                     lua_rawget(L, -2);
326                     if (!lua_istable(L, -1)) {
327                         lua_pop(L, 1);
328                         lua_newtable(L);
329                         lua_pushlstring(L, s, e-s);
330                         lua_pushvalue(L, -2);
331                         lua_rawset(L, -4);
332                     }
333                     lua_remove(L, -2);
334                     break;
335                 }
336             }
337             if (*e == '\0')
338                 break;
339             if (!lua_istable(L, -1)) {
340                 lua_pop(L, 1);
341                 ret = -1;
342                 break;
343             }
344             s = e+1;
345         }
346         e++;
347     }
348     free(buf);
349
350     return ret;
351 }
352
353 void rpmluaDelVar(rpmlua _lua, const char *key, ...)
354 {
355     INITSTATE(_lua, lua);
356     va_list va;
357     va_start(va, key);
358     (void) findkey(lua->L, FINDKEY_REMOVE, key, va);
359     va_end(va);
360 }
361
362 int rpmluaVarExists(rpmlua _lua, const char *key, ...)
363 {
364     INITSTATE(_lua, lua);
365     lua_State *L = lua->L;
366     int ret = 0;
367     va_list va;
368     va_start(va, key);
369     if (findkey(L, FINDKEY_RETURN, key, va) == 0) {
370         if (!lua_isnil(L, -1))
371             ret = 1;
372         lua_pop(L, 1);
373     }
374     va_end(va);
375     return ret;
376 }
377
378 void rpmluaPushTable(rpmlua _lua, const char *key, ...)
379 {
380     INITSTATE(_lua, lua);
381     va_list va;
382     va_start(va, key);
383     (void) findkey(lua->L, FINDKEY_CREATE, key, va);
384     lua->pushsize++;
385     va_end(va);
386 }
387
388 void rpmluaPop(rpmlua _lua)
389 {
390     INITSTATE(_lua, lua);
391     assert(lua->pushsize > 0);
392     lua->pushsize--;
393     lua_pop(lua->L, 1);
394 }
395
396 rpmluav rpmluavNew(void)
397 {
398     rpmluav var = (rpmluav) xcalloc(1, sizeof(*var));
399     return var;
400 }
401
402 rpmluav rpmluavFree(rpmluav var)
403 {
404     free(var);
405     return NULL;
406 }
407
408 void rpmluavSetListMode(rpmluav var, int flag)
409 {
410     var->listmode = flag;
411     var->keyType = RPMLUAV_NIL;
412 }
413
414 void rpmluavSetKey(rpmluav var, rpmluavType type, const void *value)
415 {
416     var->keyType = type;
417     switch (type) {
418         case RPMLUAV_NUMBER:
419             var->key.num = *((double *)value);
420             break;
421         case RPMLUAV_STRING:
422             var->key.str = (char *)value;
423             break;
424         default:
425             break;
426     }
427 }
428
429 void rpmluavSetValue(rpmluav var, rpmluavType type, const void *value)
430 {
431     var->valueType = type;
432     switch (type) {
433         case RPMLUAV_NUMBER:
434             var->value.num = *((const double *)value);
435             break;
436         case RPMLUAV_STRING:
437             var->value.str = (const char *)value;
438             break;
439         default:
440             break;
441     }
442 }
443
444 void rpmluavGetKey(rpmluav var, rpmluavType *type, void **value)
445 {
446     *type = var->keyType;
447     switch (var->keyType) {
448         case RPMLUAV_NUMBER:
449             *((double **)value) = &var->key.num;
450             break;
451         case RPMLUAV_STRING:
452             *((const char **)value) = var->key.str;
453             break;
454         default:
455             break;
456     }
457 }
458
459 void rpmluavGetValue(rpmluav var, rpmluavType *type, void **value)
460 {
461     *type = var->valueType;
462     switch (var->valueType) {
463         case RPMLUAV_NUMBER:
464             *((double **)value) = &var->value.num;
465             break;
466         case RPMLUAV_STRING:
467             *((const char **)value) = var->value.str;
468             break;
469         default:
470             break;
471     }
472 }
473
474 void rpmluavSetKeyNum(rpmluav var, double value)
475 {
476     rpmluavSetKey(var, RPMLUAV_NUMBER, &value);
477 }
478
479 void rpmluavSetValueNum(rpmluav var, double value)
480 {
481     rpmluavSetValue(var, RPMLUAV_NUMBER, &value);
482 }
483
484 double rpmluavGetKeyNum(rpmluav var)
485 {
486     rpmluavType type;
487     void *value;
488     rpmluavGetKey(var, &type, &value);
489     if (type == RPMLUAV_NUMBER)
490         return *((double *)value);
491     return (double) 0;
492 }
493
494 double rpmluavGetValueNum(rpmluav var)
495 {
496     rpmluavType type;
497     void *value;
498     rpmluavGetValue(var, &type, &value);
499     if (type == RPMLUAV_NUMBER)
500         return *((double *)value);
501     return (double) 0;
502 }
503
504 int rpmluavKeyIsNum(rpmluav var)
505 {
506     return (var->keyType == RPMLUAV_NUMBER) ? 1 : 0;
507 }
508
509 int rpmluavValueIsNum(rpmluav var)
510 {
511     return (var->valueType == RPMLUAV_NUMBER) ? 1 : 0;
512 }
513
514 int rpmluaCheckScript(rpmlua _lua, const char *script, const char *name)
515 {
516     INITSTATE(_lua, lua);
517     lua_State *L = lua->L;
518     int ret = 0;
519     if (name == NULL)
520         name = "<lua>";
521     if (luaL_loadbuffer(L, script, strlen(script), name) != 0) {
522         rpmlog(RPMLOG_ERR,
523                 _("invalid syntax in lua scriptlet: %s\n"),
524                   lua_tostring(L, -1));
525         ret = -1;
526     }
527     lua_pop(L, 1); /* Error or chunk. */
528     return ret;
529 }
530
531 int rpmluaRunScript(rpmlua _lua, const char *script, const char *name)
532 {
533     INITSTATE(_lua, lua);
534     lua_State *L = lua->L;
535     int ret = 0;
536     if (name == NULL)
537         name = "<lua>";
538     if (script == NULL)
539         script = "";
540     if (luaL_loadbuffer(L, script, strlen(script), name) != 0) {
541         rpmlog(RPMLOG_ERR, _("invalid syntax in lua script: %s\n"),
542                  lua_tostring(L, -1));
543         lua_pop(L, 1);
544         ret = -1;
545     } else if (lua_pcall(L, 0, 0, 0) != 0) {
546         rpmlog(RPMLOG_ERR, _("lua script failed: %s\n"),
547                  lua_tostring(L, -1));
548         lua_pop(L, 1);
549         ret = -1;
550     }
551     return ret;
552 }
553
554 int rpmluaRunScriptFile(rpmlua _lua, const char *filename)
555 {
556     INITSTATE(_lua, lua);
557     lua_State *L = lua->L;
558     int ret = 0;
559     if (luaL_loadfile(L, filename) != 0) {
560         rpmlog(RPMLOG_ERR, _("invalid syntax in lua file: %s\n"),
561                  lua_tostring(L, -1));
562         lua_pop(L, 1);
563         ret = -1;
564     } else if (lua_pcall(L, 0, 0, 0) != 0) {
565         rpmlog(RPMLOG_ERR, _("lua script failed: %s\n"),
566                  lua_tostring(L, -1));
567         lua_pop(L, 1);
568         ret = -1;
569     }
570     return ret;
571 }
572
573 /* From lua.c */
574 static int rpmluaReadline(lua_State *L, const char *prompt)
575 {
576    static char buffer[1024];
577    if (prompt) {
578       (void) fputs(prompt, stdout);
579       (void) fflush(stdout);
580    }
581    if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
582       return 0;  /* read fails */
583    } else {
584       lua_pushstring(L, buffer);
585       return 1;
586    }
587 }
588
589 /* Based on lua.c */
590 static void _rpmluaInteractive(lua_State *L)
591 {
592    (void) fputs("\n", stdout);
593    printf("RPM Interactive %s Interpreter\n", LUA_VERSION);
594    for (;;) {
595       int rc = 0;
596
597       if (rpmluaReadline(L, "> ") == 0)
598          break;
599       if (lua_tostring(L, -1)[0] == '=') {
600          (void) lua_pushfstring(L, "print(%s)", lua_tostring(L, -1)+1);
601          lua_remove(L, -2);
602       }
603       for (;;) {
604          rc = luaL_loadbuffer(L, lua_tostring(L, -1),
605                               lua_strlen(L, -1), "<lua>");
606          if (rc == LUA_ERRSYNTAX &&
607              strstr(lua_tostring(L, -1), "near `<eof>'") != NULL) {
608             if (rpmluaReadline(L, ">> ") == 0)
609                break;
610             lua_remove(L, -2); /* Remove error */
611             lua_concat(L, 2);
612             continue;
613          }
614          break;
615       }
616       if (rc == 0)
617          rc = lua_pcall(L, 0, 0, 0);
618       if (rc != 0) {
619          fprintf(stderr, "%s\n", lua_tostring(L, -1));
620          lua_pop(L, 1);
621       }
622       lua_pop(L, 1); /* Remove line */
623    }
624    (void) fputs("\n", stdout);
625 }
626
627 void rpmluaInteractive(rpmlua _lua)
628 {
629     INITSTATE(_lua, lua);
630     _rpmluaInteractive(lua->L);
631 }
632
633 /* ------------------------------------------------------------------ */
634 /* Lua API */
635
636 static int rpm_b64encode(lua_State *L)
637 {
638     const char *str = luaL_checkstring(L, 1);
639     size_t len = lua_strlen(L, 1);
640     int linelen = -1;
641     if (lua_gettop(L) == 2)
642         linelen = luaL_checkinteger(L, 2);
643     if (str && len) {
644         char *data = rpmBase64Encode(str, len, linelen);
645         lua_pushstring(L, data);
646         free(data);
647     }
648     return 1;
649 }
650
651 static int rpm_b64decode(lua_State *L)
652 {
653     const char *str = luaL_checkstring(L, 1);
654     if (str) {
655         void *data = NULL;
656         size_t len = 0;
657         if (rpmBase64Decode(str, &data, &len) == 0) {
658             lua_pushlstring(L, data, len);
659         } else {
660             lua_pushnil(L);
661         }
662         free(data);
663     }
664     return 1;
665 }
666
667 static int rpm_expand(lua_State *L)
668 {
669     const char *str = luaL_checkstring(L, 1);
670     char *val = NULL;
671     if (rpmExpandMacros(NULL, str, &val, 0) < 0)
672         return luaL_error(L, "error expanding macro");
673     lua_pushstring(L, val);
674     free(val);
675     return 1;
676 }
677
678 static int rpm_define(lua_State *L)
679 {
680     const char *str = luaL_checkstring(L, 1);
681     if (rpmDefineMacro(NULL, str, 0))
682         return luaL_error(L, "error defining macro");
683     return 0;
684 }
685
686 static int rpm_undefine(lua_State *L)
687 {
688     const char *str = luaL_checkstring(L, 1);
689     rpmPopMacro(NULL, str);
690     return 0;
691 }
692
693 static int rpm_load(lua_State *L)
694 {
695     const char *str = luaL_checkstring(L, 1);
696     int rc = rpmLoadMacroFile(NULL, str);
697     lua_pushnumber(L, rc);
698     return 1;
699 }
700
701 static int rpm_interactive(lua_State *L)
702 {
703     _rpmluaInteractive(L);
704     return 0;
705 }
706
707 static int rpm_next_file(lua_State *L)
708 {
709     if (nextFileFunc)
710         lua_pushstring(L, nextFileFunc(nextFileFuncParam));
711     else
712         lua_pushstring(L, NULL);
713
714     return 1;
715 }
716
717 typedef struct rpmluaHookData_s {
718     lua_State *L;
719     int funcRef;
720     int dataRef;
721 } * rpmluaHookData;
722
723 static int rpmluaHookWrapper(rpmhookArgs args, void *data)
724 {
725     rpmluaHookData hookdata = (rpmluaHookData)data;
726     lua_State *L = hookdata->L;
727     int ret = 0;
728     int i;
729     lua_rawgeti(L, LUA_REGISTRYINDEX, hookdata->funcRef);
730     lua_newtable(L);
731     for (i = 0; i != args->argc; i++) {
732         switch (args->argt[i]) {
733             case 's':
734                 lua_pushstring(L, args->argv[i].s);
735                 lua_rawseti(L, -2, i+1);
736                 break;
737             case 'i':
738                 lua_pushnumber(L, (lua_Number)args->argv[i].i);
739                 lua_rawseti(L, -2, i+1);
740                 break;
741             case 'f':
742                 lua_pushnumber(L, (lua_Number)args->argv[i].f);
743                 lua_rawseti(L, -2, i+1);
744                 break;
745             case 'p':
746                 lua_pushlightuserdata(L, args->argv[i].p);
747                 lua_rawseti(L, -2, i+1);
748                 break;
749             default:
750                 (void) luaL_error(L, "unsupported type '%c' as "
751                               "a hook argument\n", args->argt[i]);
752                 break;
753         }
754     }
755     if (lua_pcall(L, 1, 1, 0) != 0) {
756         rpmlog(RPMLOG_ERR, _("lua hook failed: %s\n"),
757                  lua_tostring(L, -1));
758         lua_pop(L, 1);
759     } else {
760         if (lua_isnumber(L, -1))
761             ret = (int)lua_tonumber(L, -1);
762         lua_pop(L, 1);
763     }
764     return ret;
765 }
766
767 static int rpm_register(lua_State *L)
768 {
769     if (!lua_isstring(L, 1)) {
770         (void) luaL_argerror(L, 1, "hook name expected");
771     } else if (!lua_isfunction(L, 2)) {
772         (void) luaL_argerror(L, 2, "function expected");
773     } else {
774         rpmluaHookData hookdata =
775             lua_newuserdata(L, sizeof(struct rpmluaHookData_s));
776         lua_pushvalue(L, -1);
777         hookdata->dataRef = luaL_ref(L, LUA_REGISTRYINDEX);
778         lua_pushvalue(L, 2);
779         hookdata->funcRef = luaL_ref(L, LUA_REGISTRYINDEX);
780         hookdata->L = L;
781         rpmhookRegister(lua_tostring(L, 1), rpmluaHookWrapper, hookdata);
782         return 1;
783     }
784     return 0;
785 }
786
787 static int rpm_unregister(lua_State *L)
788 {
789     if (!lua_isstring(L, 1)) {
790         (void) luaL_argerror(L, 1, "hook name expected");
791     } else if (!lua_isuserdata(L, 2)) {
792         (void) luaL_argerror(L, 2, "hook information expected");
793     } else {
794         rpmluaHookData hookdata = (rpmluaHookData)lua_touserdata(L, 2);
795         luaL_unref(L, LUA_REGISTRYINDEX, hookdata->funcRef);
796         luaL_unref(L, LUA_REGISTRYINDEX, hookdata->dataRef);
797         rpmhookUnregister(lua_tostring(L, 1), rpmluaHookWrapper, hookdata);
798     }
799     return 0;
800 }
801
802 static int rpm_call(lua_State *L)
803 {
804     if (!lua_isstring(L, 1)) {
805         (void) luaL_argerror(L, 1, "hook name expected");
806     } else {
807         rpmhookArgs args = rpmhookArgsNew(lua_gettop(L)-1);
808         const char *name = lua_tostring(L, 1);
809         char *argt = (char *)xmalloc(args->argc+1);
810         int i;
811         for (i = 0; i != args->argc; i++) {
812             switch (lua_type(L, i+1)) {
813                 case LUA_TNIL:
814                     argt[i] = 'p';
815                     args->argv[i].p = NULL;
816                     break;
817                 case LUA_TNUMBER: {
818                     float f = (float)lua_tonumber(L, i+1);
819                     if (f == (int)f) {
820                         argt[i] = 'i';
821                         args->argv[i].i = (int)f;
822                     } else {
823                         argt[i] = 'f';
824                         args->argv[i].f = f;
825                     }
826                 }   break;
827                 case LUA_TSTRING:
828                     argt[i] = 's';
829                     args->argv[i].s = lua_tostring(L, i+1);
830                     break;
831                 case LUA_TUSERDATA:
832                 case LUA_TLIGHTUSERDATA:
833                     argt[i] = 'p';
834                     args->argv[i].p = lua_touserdata(L, i+1);
835                     break;
836                 default:
837                     (void) luaL_error(L, "unsupported Lua type passed to hook");
838                     argt[i] = 'p';
839                     args->argv[i].p = NULL;
840                     break;
841             }
842         }
843         args->argt = argt;
844         rpmhookCallArgs(name, args);
845         free(argt);
846         (void) rpmhookArgsFree(args);
847     }
848     return 0;
849 }
850
851 /* Based on luaB_print. */
852 static int rpm_print (lua_State *L)
853 {
854     rpmlua lua = (rpmlua)getdata(L, "lua");
855     int n = lua_gettop(L);  /* number of arguments */
856     int i;
857     if (!lua) return 0;
858     lua_getglobal(L, "tostring");
859     for (i = 1; i <= n; i++) {
860         const char *s;
861         lua_pushvalue(L, -1);  /* function to be called */
862         lua_pushvalue(L, i);   /* value to print */
863         lua_call(L, 1, 1);
864         s = lua_tostring(L, -1);  /* get result */
865         if (s == NULL)
866             return luaL_error(L, "`tostring' must return a string to `print'");
867         if (lua->printbuf) {
868             rpmluapb prbuf = lua->printbuf;
869             int sl = lua_strlen(L, -1);
870             if (prbuf->used+sl+1 > prbuf->alloced) {
871                 prbuf->alloced += sl+512;
872                 prbuf->buf = xrealloc(prbuf->buf, prbuf->alloced);
873             }
874             if (i > 1)
875                 prbuf->buf[prbuf->used++] = '\t';
876             memcpy(prbuf->buf+prbuf->used, s, sl+1);
877             prbuf->used += sl;
878         } else {
879             if (i > 1)
880                 (void) fputs("\t", stdout);
881             (void) fputs(s, stdout);
882         }
883         lua_pop(L, 1);  /* pop result */
884     }
885     if (!lua->printbuf) {
886         (void) fputs("\n", stdout);
887     } else {
888         rpmluapb prbuf = lua->printbuf;
889         if (prbuf->used+1 > prbuf->alloced) {
890             prbuf->alloced += 512;
891             prbuf->buf = xrealloc(prbuf->buf, prbuf->alloced);
892         }
893         prbuf->buf[prbuf->used] = '\0';
894     }
895     return 0;
896 }
897
898 static const luaL_Reg rpmlib[] = {
899     {"b64encode", rpm_b64encode},
900     {"b64decode", rpm_b64decode},
901     {"expand", rpm_expand},
902     {"define", rpm_define},
903     {"undefine", rpm_undefine},
904     {"load", rpm_load},
905     {"register", rpm_register},
906     {"unregister", rpm_unregister},
907     {"call", rpm_call},
908     {"interactive", rpm_interactive},
909     {"next_file", rpm_next_file},
910     {NULL, NULL}
911 };
912
913 static int luaopen_rpm(lua_State *L)
914 {
915     lua_pushglobaltable(L);
916     luaL_openlib(L, "rpm", rpmlib, 0);
917     return 0;
918 }
919 #endif  /* WITH_LUA */
920