Reduce differences with root_skels in contrib.
[dragonfly.git] / contrib / bsdinstaller-1.1.6 / src / lib / lua / dfui / dfui.c
1 /*
2  * Copyright (c) 2004 Scott Ullrich <GeekGod@GeekGod.com> 
3  * Portions Copyright (c) 2004 Chris Pressey <cpressey@catseye.mine.nu>
4  *
5  * Copyright (c) 2004 The DragonFly Project.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The DragonFly Project
9  * by Scott Ullrich and Chris Pressey (see above for e-mail addresses).
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in
20  *    the documentation and/or other materials provided with the
21  *    distribution.
22  *
23  * 3. Neither the name of The DragonFly Project nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific, prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
30  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
31  * COPYRIGHT HOLDERS, CONTRIBUTORS OR VOICES IN THE AUTHOR'S HEAD
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY
33  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
34  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
36  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
37  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
38  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
40  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
41  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
43  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  */
48
49 /* 
50  * dfui.c - libdfui Bindings for Lua
51  * $Id: dfui.c,v 1.64 2005/04/04 13:56:37 den Exp $
52  */
53
54 #include <stdio.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 #include "dfui/dfui.h"
59 #include "dfui/dump.h"
60 #include "dfui/system.h"
61
62 #include "lua50/lua.h"
63 #include "lua50/lauxlib.h"
64 #include "lua50/lualib.h"
65
66 #include "lua_dfui.h"
67
68 LUA_CHECK_FUNCTION(dfui_connection, "DFUIConnection", struct dfui_connection *)
69 LUA_PUSH_FUNCTION(dfui_connection, "DFUIConnection", struct dfui_connection *)
70
71 #define DFUI_OBJ_FORM   1
72 #define DFUI_OBJ_FIELD  2
73 #define DFUI_OBJ_ACTION 3
74
75 static void
76 dfui_field_options_from_lua_table(lua_State *L, struct dfui_field *fi)
77 {
78         lua_pushnil(L);
79         while (lua_next(L, -2) != 0) {
80                 if (lua_isstring(L, -1)) {
81                         dfui_field_option_add(fi, lua_tostring(L, -1));
82                 }
83                 lua_pop(L, 1);
84         }
85 }
86
87 static void
88 set_dfui_properties_from_lua_table(lua_State *L, int table_idx,
89                                    int dfui_obj_type, void *dfui_obj)
90 {
91         const char *key, *value;
92
93         /*
94          * Traverse the table, looking for key->value pairs that we can use
95          * to modify the field.
96          * For each entry, if it is standard (id, name, short_desc, long_desc)
97          * ignore it; if it is anything else, assume it is a property.
98          */
99         lua_pushnil(L);
100         while (lua_next(L, table_idx) != 0) {
101                 if (lua_isstring(L, -2) && lua_isstring(L, -1)) {
102                         key = lua_tostring(L, -2);
103                         value = lua_tostring(L, -1);
104
105                         if (strcmp(key, "id") == 0 ||
106                             strcmp(key, "name") == 0 ||
107                             strcmp(key, "short_desc") == 0 ||
108                             strcmp(key, "long_desc") == 0) {
109                                 /*
110                                  * Skip it, we've already done it.
111                                  */
112                         } else if (strcmp(key, "multiple") == 0 &&
113                             dfui_obj_type == DFUI_OBJ_FORM) {
114                                 dfui_form_set_multiple(
115                                     (struct dfui_form *)dfui_obj,
116                                     strcmp(value, "true") == 0
117                                 );
118                         } else if (strcmp(key, "extensible") == 0 &&
119                             dfui_obj_type == DFUI_OBJ_FORM) {
120                                 dfui_form_set_extensible(
121                                     (struct dfui_form *)dfui_obj,
122                                     strcmp(value, "true") == 0
123                                 );
124                         } else {
125                                 /*
126                                  * It's a property.
127                                  */
128                                 switch (dfui_obj_type) {
129                                 case DFUI_OBJ_FORM:
130                                         dfui_form_property_set(
131                                             (struct dfui_form *)dfui_obj,
132                                             key, value);
133                                         break;
134                                 case DFUI_OBJ_FIELD:
135                                         dfui_field_property_set(
136                                             (struct dfui_field *)dfui_obj,
137                                             key, value);
138                                         break;
139                                 case DFUI_OBJ_ACTION:
140                                         dfui_action_property_set(
141                                             (struct dfui_action *)dfui_obj,
142                                             key, value);
143                                         break;
144                                 }
145                         }
146                 } else if (lua_isstring(L, -2) && lua_istable(L, -1)) {
147                         key = lua_tostring(L, -2);
148                         if (strcmp(key, "options") == 0 &&
149                             dfui_obj_type == DFUI_OBJ_FIELD) {
150                                 dfui_field_options_from_lua_table(L,
151                                     (struct dfui_field *)dfui_obj
152                                 );
153                         }
154                 } else {
155                         /*
156                          * Either the key or the value is not a string,
157                          * so just skip it.
158                          */
159                 }
160
161                 /*
162                  * Remove the value, but leave the key for the next iteration.
163                  */
164                 lua_pop(L, 1);
165         }
166 }
167
168 /*** TRANSLATORS ***/
169
170 /*
171  * Pop a Lua table representing a DFUI action from the Lua stack,
172  * create a new DFUI action from it, and return it.
173  */
174 static struct dfui_action *
175 dfui_action_from_lua_table(lua_State *L, int table_idx)
176 {
177         struct dfui_action *a;
178         const char *id, *name, *short_desc, *long_desc;
179
180         /*
181          * Get the basic properties of the action.
182          */
183         id = lua_access_table_string(L, table_idx, "id");
184         name = lua_access_table_string(L, table_idx, "name");
185         short_desc = lua_access_table_string(L, table_idx, "short_desc");
186         long_desc = lua_access_table_string(L, table_idx, "long_desc");
187
188         /*
189          * Create the initial action.
190          */
191         a = dfui_action_new(id,
192             dfui_info_new(name, short_desc, long_desc));
193
194         set_dfui_properties_from_lua_table(L, table_idx, DFUI_OBJ_ACTION, a);
195
196         lua_pop(L, 1);
197         return(a);
198 }
199
200 /*
201  * Pop a Lua table representing a DFUI field from the Lua stack,
202  * create a new DFUI field from it, and return it.
203  */
204 static struct dfui_field *
205 dfui_field_from_lua_table(lua_State *L, int table_idx)
206 {
207         struct dfui_field *fi;
208         const char *id, *name, *short_desc, *long_desc;
209
210         /*
211          * Get the basic properties of the field.
212          */
213         id = lua_access_table_string(L, table_idx, "id");
214         name = lua_access_table_string(L, table_idx, "name");
215         short_desc = lua_access_table_string(L, table_idx, "short_desc");
216         long_desc = lua_access_table_string(L, table_idx, "long_desc");
217
218         /*
219          * Create the initial field.
220          */
221         fi = dfui_field_new(id,
222             dfui_info_new(name, short_desc, long_desc));
223
224         set_dfui_properties_from_lua_table(L, table_idx, DFUI_OBJ_FIELD, fi);
225
226         lua_pop(L, 1);
227         return(fi);
228 }
229
230 /*
231  * Pop a Lua table representing a DFUI dataset from the Lua stack,
232  * create a new DFUI dataset from it, and return it.
233  */
234 static struct dfui_dataset *
235 dfui_dataset_from_lua_table(lua_State *L, int table_idx)
236 {
237         struct dfui_dataset *ds;
238
239         /*
240          * Create the initial dataset.
241          */
242         ds = dfui_dataset_new();
243
244         /*
245          * Traverse the table, looking for key->value pairs that we can use.
246          */
247         lua_pushnil(L);
248         while (lua_next(L, table_idx) != 0) {
249                 if (lua_isstring(L, -2) && lua_isstring(L, -1)) {
250                         dfui_dataset_celldata_add(ds,
251                             lua_tostring(L, -2), lua_tostring(L, -1)
252                         );
253                 } else {
254                         /* Bogus, just skip it */
255                 }
256
257                 /*
258                  * Remove the value, but leave the key for the next iteration.
259                  */
260                 lua_pop(L, 1);
261         }       
262
263         /*
264          * Remove the table.
265          */
266         lua_pop(L, 1);
267         return(ds);
268 }
269
270 /*
271  * Pop a Lua table representing a DFUI form from the Lua stack,
272  * create a new DFUI form from it, and return it.
273  */
274 static struct dfui_form *
275 dfui_form_from_lua_table(lua_State *L, int table_idx)
276 {
277         struct dfui_form *f;
278         struct dfui_action *a;
279         struct dfui_field *fi;
280         struct dfui_dataset *ds;
281         const char *id, *name, *short_desc, *long_desc;
282         int list_idx, subtable_idx, counter, done;
283
284         /*
285          * Get the basic properties of the form.
286          */
287         id = lua_access_table_string(L, table_idx, "id");
288         name = lua_access_table_string(L, table_idx, "name");
289         short_desc = lua_access_table_string(L, table_idx, "short_desc");
290         long_desc = lua_access_table_string(L, table_idx, "long_desc");
291
292         /*
293          * Create the initial form.
294          */
295         f = dfui_form_new(id, dfui_info_new(name, short_desc, long_desc));
296
297         set_dfui_properties_from_lua_table(L, table_idx, DFUI_OBJ_FORM, f);
298
299         /*
300          * Get the list of actions attached to the form.
301          */
302         lua_pushliteral(L, "actions");
303         lua_gettable(L, table_idx);
304         list_idx = lua_gettop(L);
305         if (lua_istable(L, list_idx)) {
306                 /*
307                  * Loop through all entries in this table, creating
308                  * and attaching a new action for each one.
309                  */
310                 counter = 1;
311                 done = 0;
312                 while (!done) {
313                         lua_pushnumber(L, counter++);
314                         lua_gettable(L, list_idx);
315                         subtable_idx = lua_gettop(L);
316                         if (lua_istable(L, subtable_idx)) {
317                                 a = dfui_action_from_lua_table(L, subtable_idx);
318                                 dfui_form_action_attach(f, a);
319                         } else {
320                                 done = 1;
321                         }
322                 }
323         } else {
324                 /* No actions */
325         }
326         lua_pop(L, 1);
327
328         /*
329          * Get the list of fields attached to the form.
330          */
331         lua_pushliteral(L, "fields");
332         lua_gettable(L, table_idx);
333         list_idx = lua_gettop(L);
334         if (lua_istable(L, list_idx)) {
335                 /*
336                  * Loop through all entries in this table, creating
337                  * and attaching a new field for each one.
338                  */
339                 counter = 1;
340                 done = 0;
341                 while (!done) {
342                         lua_pushnumber(L, counter++);
343                         lua_gettable(L, list_idx);
344                         subtable_idx = lua_gettop(L);
345                         if (lua_istable(L, subtable_idx)) {
346                                 fi = dfui_field_from_lua_table(L, subtable_idx);
347                                 dfui_form_field_attach(f, fi);
348                         } else {
349                                 done = 1;
350                         }
351                 }
352         } else {
353                 /* No fields */
354         }
355         lua_pop(L, 1);
356
357         /*
358          * Get the list of datasets attached to the form.
359          */
360         lua_pushliteral(L, "datasets");
361         lua_gettable(L, table_idx);
362         list_idx = lua_gettop(L);
363         if (lua_istable(L, list_idx)) {
364                 /*
365                  * Loop through all entries in this table, creating
366                  * and attaching a new dataset for each one.
367                  */
368                 counter = 1;
369                 done = 0;
370                 while (!done) {
371                         lua_pushnumber(L, counter++);
372                         lua_gettable(L, list_idx);
373                         subtable_idx = lua_gettop(L);
374                         if (lua_istable(L, subtable_idx)) {
375                                 ds = dfui_dataset_from_lua_table(L, subtable_idx);
376                                 dfui_form_dataset_add(f, ds);
377                         } else {
378                                 done = 1;
379                         }
380                 }
381         } else {
382                 /* No datasets */
383         }
384         lua_pop(L, 1);
385
386         /*
387          * Finally, delete the table representing the form by
388          * popping it from the top of the stack.
389          */
390         lua_pop(L, 1);
391
392         return(f);
393 }
394
395 /*
396  * Push a new Lua table representing the given DFUI response
397  * onto the Lua stack.
398  */
399 static int
400 lua_table_from_dfui_response(lua_State *L, struct dfui_response *r)
401 {
402         int table_idx, list_idx, subtable_idx;
403         struct dfui_dataset *ds;
404         struct dfui_celldata *cd;
405         const char *value;
406         int counter = 1;
407         const char *f_id, *a_id;
408
409         lua_newtable(L);
410         table_idx = lua_gettop(L);
411
412         /*
413          * Add response id's to the table.
414          */
415         f_id = dfui_response_get_form_id(r);
416         a_id = dfui_response_get_action_id(r);
417
418         lua_pushliteral(L, "form_id");
419         lua_pushlstring(L, f_id, strlen(f_id));
420         lua_settable(L, table_idx);
421
422         lua_pushliteral(L, "action_id");
423         lua_pushlstring(L, a_id, strlen(a_id));
424         lua_settable(L, table_idx);
425
426         /*
427          * Create 'datasets' lists to the table.
428          */
429         lua_pushliteral(L, "datasets");
430         lua_newtable(L);
431         list_idx = lua_gettop(L);
432         
433         /*
434          * Add response datasets to the 'datasets' list.
435          */
436         for (ds = dfui_response_dataset_get_first(r); ds != NULL;
437              ds = dfui_dataset_get_next(ds)) {
438                 lua_pushnumber(L, counter++);
439                 lua_newtable(L);
440                 subtable_idx = lua_gettop(L);
441                 /*
442                  * Populate this subtable with the celldatas...
443                  */
444                 for (cd = dfui_dataset_celldata_get_first(ds); cd != NULL;
445                      cd = dfui_celldata_get_next(cd)) {
446                         f_id = dfui_celldata_get_field_id(cd);
447                         value = dfui_celldata_get_value(cd);
448                         lua_pushlstring(L, f_id, strlen(f_id));
449                         lua_pushlstring(L, value, strlen(value));
450                         lua_settable(L, subtable_idx);
451                 }
452                 /*
453                  * Add this subtable to the list
454                  */
455                 lua_settable(L, list_idx);
456         }
457
458         /*
459          * Add the 'datasets' list to the table.
460          */
461         lua_settable(L, table_idx);
462
463         return(table_idx);
464 }
465
466 /*** CONSTRUCTOR & DESTRUCTOR ***/
467
468 static int
469 lua_dfui_connection_new(lua_State *L)
470 {
471         const char *transport_string, *rendezvous;
472         int transport;
473         struct dfui_connection *c;
474
475         transport_string = luaL_checkstring(L, 1);
476         rendezvous = luaL_checkstring(L, 2);
477         if (! (transport = get_transport(transport_string)) > 0) {
478                 lua_pushnil(L);
479                 return(1);
480         }
481         c = dfui_connection_new(transport, rendezvous);
482         lua_push_dfui_connection(L, c);
483         return(1);
484 }
485
486 static int
487 lua_dfui_connection_destroy(lua_State *L)
488 {
489         struct dfui_connection *c;
490
491         c = (struct dfui_connection *)lua_unboxpointer(L, 1);
492         if (c != NULL) {
493                 dfui_be_stop(c);
494                 dfui_connection_free(c);
495         }
496         return(0);
497 }
498
499 /*** BOUND METHODS ***/
500
501 static int
502 lua_dfui_be_start(lua_State *L)
503 {
504         struct dfui_connection *c;
505         int result;
506
507         c = lua_check_dfui_connection(L, 1);
508         result = dfui_be_start(c);
509         lua_pushnumber(L, result);
510
511         return(1);
512 }
513
514 static int
515 lua_dfui_be_stop(lua_State *L)
516 {
517         struct dfui_connection *c;
518         int result;
519
520         c = lua_check_dfui_connection(L, 1);
521         result = dfui_be_stop(c);
522         lua_pushnumber(L, result);
523
524         return(1);
525 }
526
527 static int
528 lua_dfui_be_present(lua_State *L)
529 {
530         struct dfui_connection *c;
531         struct dfui_form *f;
532         struct dfui_response *r;
533         int response_table_idx, actions_list_idx, action_table_idx;
534         const char *a_a_id, *r_a_id;
535
536         c = lua_check_dfui_connection(L, 1);
537         luaL_checktype(L, 2, LUA_TTABLE);
538
539         f = dfui_form_from_lua_table(L, 2);
540
541         if (dfui_be_present(c, f, &r)) {
542                 response_table_idx = lua_table_from_dfui_response(L, r);
543
544                 r_a_id = dfui_response_get_action_id(r);
545
546                 /*
547                  * Handle the 'effect' key which may be given in
548                  * any action table within a form table.  When it
549                  * is given, it should be a function which the
550                  * user wishes to be executed automatically when
551                  * the response is caused by that action.  This lets
552                  * the user write simpler Lua code (c:present(f) can
553                  * execute things directly, instead of returning an
554                  * id code which the user must look up in a table etc.)
555                  */
556                 /*
557                  * First, look for an 'actions' list in the form table.
558                  */
559                 lua_pushliteral(L, "actions");
560                 lua_gettable(L, 2);
561                 actions_list_idx = lua_gettop(L);
562                 if (lua_istable(L, actions_list_idx)) {
563                         int i = 1;
564                         int done = 0;
565
566                         /*
567                          * Look in the 'actions' list for
568                          * action tables.
569                          */
570                         while (!done) {
571                                 lua_rawgeti(L, actions_list_idx, i);
572                                 action_table_idx = lua_gettop(L);
573                                 if (lua_istable(L, action_table_idx)) {
574                                         /*
575                                          * See if this action's 'id'
576                                          * is the response's action_id
577                                          * (which we saved above.)
578                                          */
579                                         a_a_id = lua_access_table_string(L,
580                                             action_table_idx, "id");
581                                         if (strcmp(r_a_id, a_a_id) == 0) {
582                                                 /*
583                                                  * It is.  So, see if action
584                                                  * table has an 'effect' key.
585                                                  */
586                                                 lua_pushliteral(L, "result");
587                                                 lua_pushliteral(L, "effect");
588                                                 lua_gettable(L, action_table_idx);
589                                                 if (lua_isfunction(L, lua_gettop(L))) {
590                                                         /*
591                                                          * It is, and it's a function.
592                                                          * Execute it.
593                                                          */
594                                                         lua_call(L, 0, 1);
595                                                         lua_rawset(L, response_table_idx);
596                                                         done = 1;
597                                                 } else {
598                                                         lua_pop(L, 2);
599                                                 }
600                                         }
601                                 } else {
602                                         done = 1;
603                                 }
604                                 lua_pop(L, 1);  /* remove the action table */
605                                 i++;
606                         }
607                 }
608                 lua_pop(L, 1);  /* remove the 'actions' list */
609         } else {
610                 lua_pushnil(L);
611         }
612
613         dfui_response_free(r);
614         dfui_form_free(f);
615         return(1);
616 }
617
618 /**** Binding Tables ****/
619
620 const luaL_reg dfui_connection_methods[] = {
621         {"new",         lua_dfui_connection_new },
622         {"start",       lua_dfui_be_start },
623         {"stop",        lua_dfui_be_stop },
624         {"present",     lua_dfui_be_present },
625         {0, 0}
626 };
627
628 const luaL_reg dfui_connection_meta_methods[] = {
629         {"__gc",        lua_dfui_connection_destroy },
630         {0, 0}
631 };
632
633
634 /*** REGISTRATION ***/
635
636 LUA_API int
637 lua_dfui_register(lua_State *L)
638 {
639         luaL_openlib(L, "DFUIConnection",
640                         dfui_connection_methods, 0);        /* fill methods table */
641         luaL_openlib(L, "DFUIConnectionMeta",
642                         dfui_connection_meta_methods,  0);  /* fill metatable */
643         lua_pop(L, 1);
644
645         lua_set_instance_handler(L,
646                         "DFUIConnection", "DFUIConnectionMeta");
647
648         return(1);
649 }