13 extern struct HomeBank *GLOBALS;
14 #include "dsp_mainwindow.h"
15 #include "dsp_account.h"
16 #include "ui-transaction.h"
19 static gint ext_perl_init(int* argc, char** argv[], char** env[]);
20 static void ext_perl_term(void);
21 static gboolean ext_perl_check_file(const gchar* plugin_filepath);
22 static GHashTable* ext_perl_read_plugin_metadata(const gchar* plugin_filepath);
23 static gint ext_perl_load_plugin(const gchar* plugin_filepath);
24 static void ext_perl_unload_plugin(const gchar* plugin_filepath);
25 static void ext_perl_execute_action(const gchar* plugin_filepath);
26 static void ext_perl_call_hook(const gchar* hook_id, GList* args);
28 static SV* val_to_sv(GValue* val);
29 static GValue* sv_to_val(SV* sv);
31 static gboolean gperl_value_from_sv(GValue* value, SV* sv);
32 static SV* gperl_sv_from_value(const GValue* value, gboolean copy_boxed);
35 static inline GValue* EXT_SV(GValue* v, SV* sv, GType type)
37 g_value_init(v, type);
38 gperl_value_from_sv(v, sv);
43 #define EXT_P2C_OBJECT(PKG, ARG, VAR, TYP) \
44 if (sv_derived_from(ARG, PKG)) { \
45 IV iv = SvIV((SV*)SvRV(ARG)); \
46 VAR = INT2PTR(TYP, iv); \
48 croak(#VAR" is not of type "PKG); \
51 #define EXT_C2P_OBJECT(PKG, ARG, VAR) \
52 sv_setref_pv(ARG, PKG, (void*)VAR)
55 static inline GPtrArray* SvGptrarray(const SV* sv)
58 sv = MUTABLE_SV(SvRV(sv));
60 if (SvTYPE(sv) == SVt_PVAV) {
64 GPtrArray* array = g_ptr_array_new();
65 for (i = 0; i <= top; ++i) {
66 SV** item = av_fetch(av, i, 0);
68 g_ptr_array_add(array, sv_to_val(*item));
73 croak("var is not an array");
77 static inline SV* newSVgptrarray(const GPtrArray* a)
82 for (i = 0; i < a->len; ++i) {
83 GValue* item = g_ptr_array_index(a, i);
84 av_push(av, val_to_sv(item));
86 return newRV((SV*)av);
92 static inline GHashTable* SvGhashtable(const SV* sv)
95 sv = MUTABLE_SV(SvRV(sv));
97 if (SvTYPE(sv) == SVt_PVHV) {
103 GHashTable* hash = g_hash_table_new(g_str_hash, g_str_equal);
104 while ((item = hv_iternextsv(hv, &key, &len))) {
105 g_hash_table_insert(hash, key, sv_to_val(item));
110 croak("var is not a hash");
114 static inline SV* newSVghashtable(GHashTable* h)
119 g_hash_table_iter_init(&it, h);
122 while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&item)) {
123 hv_store(hv, key, -g_utf8_strlen(key, -1), val_to_sv(item), 0);
125 return newRV((SV*)hv);
131 static inline gboolean SvGboolean(SV* sv)
137 return !!SvIV(SvRV(sv));
143 static inline SV* newSVgboolean(gboolean b)
145 return sv_setref_iv(newSV(0), "HomeBank::Boolean", !!b);
149 static inline gchar* SvGchar_ptr(SV* sv)
151 return SvPVutf8_nolen(sv);
154 static inline SV* newSVgchar_ptr(const gchar* str)
156 if (!str) return &PL_sv_undef;
158 SV* sv = newSVpv(str, 0);
164 static inline GObject* SvGobject(const SV* sv)
166 GObject* (*func)(const SV*) = ext_symbol_lookup("gperl_get_object");
173 static inline SV* newSVgobject(const GObject* o)
175 SV* (*func)(const GObject*, gboolean) = ext_symbol_lookup("gperl_new_object");
177 return func(o, FALSE);
183 static PerlInterpreter* context = NULL;
186 static gint ext_perl_init(int* argc, char** argv[], char** env[])
190 PERL_SYS_INIT3(argc, argv, env);
191 context = perl_alloc();
192 perl_construct(context);
194 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
196 PL_perl_destruct_level = 1;
198 gchar* bootstrap = g_strdup_printf("-e"
201 "HomeBank->bootstrap;",
202 homebank_app_get_pkglib_dir());
203 char *args[] = { "", bootstrap };
205 EXTERN_C void xs_init(pTHX);
206 if (perl_parse(context, xs_init, 2, args, NULL) || perl_run(context)) {
215 static void ext_perl_term(void)
218 perl_destruct(context);
225 static gboolean ext_perl_check_file(const gchar* plugin_filepath)
227 if (g_str_has_suffix(plugin_filepath, ".pl")) {
233 static GHashTable* ext_perl_read_plugin_metadata(const gchar* plugin_filepath)
235 GHashTable* table = NULL;
237 if (!context) return NULL;
238 PERL_SET_CONTEXT(context);
244 mXPUSHs(newSVgchar_ptr(plugin_filepath));
247 int ret = call_pv("HomeBank::read_metadata", G_SCALAR | G_EVAL);
252 table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
255 sv = MUTABLE_SV(SvRV(sv));
257 if (SvTYPE(sv) == SVt_PVHV) {
263 while ((item = hv_iternextsv(hv, &key, &len))) {
265 gchar* val = SvPVutf8_nolen(item);
266 g_hash_table_insert(table, g_strdup(key), g_strdup(val));
279 static gint ext_perl_load_plugin(const gchar* plugin_filepath)
281 if (!context) return -1;
282 PERL_SET_CONTEXT(context);
288 mXPUSHs(newSVgchar_ptr(plugin_filepath));
290 call_pv("HomeBank::load_plugin", G_DISCARD | G_EVAL);
295 g_printerr("%s", SvPV_nolen(ERRSV));
306 static void ext_perl_unload_plugin(const gchar* plugin_filepath)
308 if (!context) return;
309 PERL_SET_CONTEXT(context);
315 mXPUSHs(newSVgchar_ptr(plugin_filepath));
317 call_pv("HomeBank::unload_plugin", G_DISCARD | G_EVAL);
321 g_printerr("%s", SvPV_nolen(ERRSV));
329 static void ext_perl_execute_action(const gchar* plugin_filepath)
331 if (!context) return;
332 PERL_SET_CONTEXT(context);
338 mXPUSHs(newSVgchar_ptr(plugin_filepath));
340 call_pv("HomeBank::execute_action", G_DISCARD | G_EVAL);
344 g_printerr("%s", SvPV_nolen(ERRSV));
352 static void ext_perl_call_hook(const gchar* hook_id, GList* args)
354 if (!context) return;
355 PERL_SET_CONTEXT(context);
361 mXPUSHs(newSVgchar_ptr(hook_id));
363 GList *list = g_list_first(args);
365 GValue* val = list->data;
366 XPUSHs(sv_2mortal(val_to_sv(val)));
367 list = g_list_next(list);
371 call_pv("HomeBank::call_hook", G_ARRAY);
380 static SV* val_to_sv(GValue* val)
382 if (!val || !G_IS_VALUE(val) || G_VALUE_TYPE(val) == G_TYPE_NONE) {
385 if (G_VALUE_TYPE(val) == G_TYPE_BOOLEAN) {
386 return newSVgboolean(g_value_get_boolean(val));
388 if (G_VALUE_TYPE(val) == G_TYPE_PTR_ARRAY) {
389 return newSVgptrarray((GPtrArray*)g_value_get_boxed(val));
391 if (G_VALUE_TYPE(val) == G_TYPE_HASH_TABLE) {
392 return newSVghashtable((GHashTable*)g_value_get_boxed(val));
394 #define obj(CTYPE, _2, PART, GTYPE, _5) \
395 if (G_VALUE_TYPE(val) == GTYPE) { \
397 CTYPE* ptr = (CTYPE*)g_value_get_##PART(val); \
398 EXT_C2P_OBJECT("HomeBank::"#CTYPE, sv, rc_ref(ptr)); \
401 #include "ext-value.h"
403 return gperl_sv_from_value(val, FALSE);
406 static GValue* sv_to_val(SV* sv)
408 GValue* val = g_new0(GValue, 1);
410 if (SvUOK(sv)) return EXT_SV(val, sv, G_TYPE_UINT);
411 if (SvIOK(sv)) return EXT_SV(val, sv, G_TYPE_INT);
412 if (SvNOK(sv)) return EXT_SV(val, sv, G_TYPE_DOUBLE);
413 if (SvPOK(sv)) return EXT_SV(val, sv, G_TYPE_STRING);
414 if (sv_isobject(sv)) {
415 if (sv_derived_from(sv, "HomeBank::Boolean")) {
416 return EXT_BOOLEAN(val, SvGboolean(sv));
418 #define obj(CTYPE, NAME, _3, _4, _5) \
419 if (sv_derived_from(sv, "HomeBank::"#CTYPE)) { \
421 EXT_P2C_OBJECT("HomeBank::"#CTYPE, sv, ptr, CTYPE*); \
422 return EXT_##NAME(val, ptr); \
424 #include "ext-value.h"
426 return EXT_SV(val, sv, G_TYPE_OBJECT);
430 switch (SvTYPE(sv)) {
432 return EXT_BOOLEAN(val, SvGboolean(sv));
434 return EXT_ARRAY(val, SvGptrarray(sv));
436 return EXT_HASH_TABLE(val, SvGhashtable(sv));
441 switch (SvTYPE(sv)) {
443 return EXT_ARRAY(val, SvGptrarray(sv));
445 return EXT_HASH_TABLE(val, SvGhashtable(sv));
455 static gboolean gperl_value_from_sv(GValue* value, SV* sv)
457 gboolean (*func)(GValue*, SV*) = ext_symbol_lookup("gperl_value_from_sv");
458 if (func) return func(value, sv);
460 GType type = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
461 if (!SvOK(sv)) return TRUE;
465 gchar *tmp = SvGchar_ptr(sv);
466 g_value_set_schar(value, (gint8)(tmp ? tmp[0] : 0));
471 char *tmp = SvPV_nolen(sv);
472 g_value_set_uchar(value, (guchar)(tmp ? tmp[0] : 0));
476 g_value_set_boolean(value, SvTRUE(sv));
479 g_value_set_int(value, SvIV(sv));
482 g_value_set_uint(value, SvIV(sv));
485 g_value_set_long(value, SvIV(sv));
488 g_value_set_ulong(value, SvIV(sv));
491 g_value_set_float(value, (gfloat)SvNV(sv));
494 g_value_set_double(value, SvNV(sv));
497 g_value_set_string(value, SvGchar_ptr(sv));
503 static SV* gperl_sv_from_value(const GValue* value, gboolean copy_boxed)
505 SV* (*func)(const GValue*, gboolean) = ext_symbol_lookup("gperl_sv_from_value");
506 if (func) return func(value, copy_boxed);
508 GType type = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value));
511 return newSViv(g_value_get_schar(value));
513 return newSVuv(g_value_get_uchar(value));
515 return newSViv(g_value_get_boolean(value));
517 return newSViv(g_value_get_int(value));
519 return newSVuv(g_value_get_uint(value));
521 return newSViv(g_value_get_long(value));
523 return newSVuv(g_value_get_ulong(value));
525 return newSVnv(g_value_get_float(value));
527 return newSVnv(g_value_get_double(value));
529 return newSVgchar_ptr(g_value_get_string(value));
535 static void _register(void) __attribute__((constructor));
536 static void _register()
542 ext_perl_read_plugin_metadata,
543 ext_perl_load_plugin,
544 ext_perl_unload_plugin,
545 ext_perl_execute_action,
550 MODULE = HomeBank PACKAGE = HomeBank
564 RETVAL = homebank_app_get_config_dir();
569 has(const gchar* CLASS, ...)
573 PERL_UNUSED_ARG(CLASS);
575 for (i = 1; i < items; ++i) {
576 gchar* feature = SvGchar_ptr(ST(i));
577 if (!feature || !ext_has(feature)) {
588 RETVAL = G_OBJECT(GLOBALS->mainwindow);
593 main_ui_manager(void)
595 struct hbfile_data *data;
598 if (GLOBALS->mainwindow) {
599 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GLOBALS->mainwindow, GTK_TYPE_WINDOW)), "inst_data");
601 RETVAL = G_OBJECT(data->manager);
608 info(const gchar* CLASS, const gchar* title, const gchar* text)
610 PERL_UNUSED_ARG(CLASS);
611 ext_run_modal(title, text, "info");
614 warn(const gchar* CLASS, const gchar* title, const gchar* text)
616 PERL_UNUSED_ARG(CLASS);
617 ext_run_modal(title, text, "warn");
620 error(const gchar* CLASS, const gchar* title, const gchar* text)
622 PERL_UNUSED_ARG(CLASS);
623 ext_run_modal(title, text, "error");
626 hook(const gchar* CLASS, const gchar* hook_name, ...)
631 PERL_UNUSED_ARG(CLASS);
632 for (i = 2; i < items; ++i) {
634 GValue *val = sv_to_val(sv);
635 list = g_list_append(list, val);
638 ext_vhook(hook_name, list);
640 // TODO free all the things
643 open_prefs(const gchar* CLASS)
645 PERL_UNUSED_ARG(CLASS);
646 RETVAL = G_OBJECT(defpref_dialog_new(PREF_GENERAL));
651 MODULE = HomeBank PACKAGE = HomeBank::File
654 owner(const gchar* CLASS, ...)
656 PERL_UNUSED_ARG(CLASS);
658 hbfile_change_owner(g_strdup(SvGchar_ptr(ST(1))));
660 RETVAL = GLOBALS->owner;
665 transactions(const gchar* CLASS)
667 PERL_UNUSED_ARG(CLASS);
669 GList* acc_list = g_hash_table_get_values(GLOBALS->h_acc);
670 GList* acc_link = g_list_first(acc_list);
671 for (; acc_link; acc_link = g_list_next(acc_link)) {
672 Account *acc = acc_link->data;
674 GList* txn_link = g_queue_peek_head_link(acc->txn_queue);
675 for (; txn_link; txn_link = g_list_next(txn_link)) {
676 Transaction* txn = txn_link->data;
678 GValue val = G_VALUE_INIT;
679 SV* sv = val_to_sv(EXT_TRANSACTION(&val, txn));
684 g_list_free(acc_list);
692 baz(const gchar* CLASS, Account* account)
694 PERL_UNUSED_ARG(CLASS);
695 g_print("hello: %s\n", account->name);
698 meh(const gchar* CLASS, GPtrArray* asdf)
700 PERL_UNUSED_ARG(CLASS);
701 g_print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW\n");
705 g_print("the array is nil\n");
711 g_ptr_array_unref(asdf);
714 foo(const gchar* CLASS, GHashTable* asdf)
716 PERL_UNUSED_ARG(CLASS);
717 g_print("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW\n");
720 g_hash_table_iter_init(&it, asdf);
723 while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&item)) {
724 g_print("hash with key: %s\n", key);
727 g_print("the hash is nil\n");
733 g_hash_table_unref(asdf);
736 MODULE = HomeBank PACKAGE = HomeBank::Account
739 compute_balances(const gchar* CLASS)
741 PERL_UNUSED_ARG(CLASS);
742 account_compute_balances();
747 RETVAL = da_acc_malloc();
752 DESTROY(Account* SELF)
757 get(const gchar* CLASS, guint key)
759 PERL_UNUSED_ARG(CLASS);
760 RETVAL = rc_ref(da_acc_get(key));
765 get_by_name(const gchar* CLASS, const gchar* name)
767 PERL_UNUSED_ARG(CLASS);
768 RETVAL = rc_ref(da_acc_get_by_name((gchar*)name));
773 name(Account* SELF, ...)
776 account_rename(SELF, SvGchar_ptr(ST(1)));
783 number(Account* SELF, ...)
786 g_free(SELF->number);
787 SELF->number = g_strdup(SvGchar_ptr(ST(1)));
789 RETVAL = SELF->number;
794 bankname(Account* SELF, ...)
797 g_free(SELF->bankname);
798 SELF->bankname = g_strdup(SvGchar_ptr(ST(1)));
800 RETVAL = SELF->bankname;
805 initial(Account* SELF, ...)
808 SELF->initial = SvNV(ST(1));
810 RETVAL = SELF->initial;
815 minimum(Account* SELF, ...)
818 SELF->minimum = SvNV(ST(1));
820 RETVAL = SELF->minimum;
825 cheque1(Account* SELF, ...)
831 SELF->cheque1 = SvUV(ST(1));
833 RETVAL = SELF->cheque1;
838 cheque2(Account* SELF, ...)
844 SELF->cheque2 = SvUV(ST(1));
846 RETVAL = SELF->cheque2;
851 balance(Account* SELF)
858 RETVAL = SELF->bal_bank;
861 RETVAL = SELF->bal_future;
864 RETVAL = SELF->bal_today;
871 is_inserted(Account* SELF)
873 RETVAL = da_acc_get(SELF->key) == SELF;
878 is_used(Account* SELF)
880 RETVAL = account_is_used(SELF->key);
885 insert(Account* SELF)
887 if (SELF->key == 0 || account_is_used(SELF->key))
888 RETVAL = da_acc_append(rc_ref(SELF));
890 RETVAL = da_acc_insert(rc_ref(SELF));
895 remove(Account* SELF)
897 da_acc_remove(SELF->key);
900 transactions(Account* SELF)
902 GList* list = g_queue_peek_head_link(SELF->txn_queue);
903 for (; list; list = g_list_next(list)) {
904 Transaction* txn = list->data;
905 GValue val = G_VALUE_INIT;
906 SV* sv = val_to_sv(EXT_TRANSACTION(&val, txn));
913 RETVAL = G_OBJECT(register_panel_window_new(SELF->key, SELF));
918 MODULE = HomeBank PACKAGE = HomeBank::Transaction
923 RETVAL = da_transaction_malloc();
928 DESTROY(Transaction* SELF)
930 da_transaction_free(SELF);
933 amount(Transaction* SELF, ...)
936 SELF->amount = SvNV(ST(1));
938 RETVAL = SELF->amount;
943 account_num(Transaction* SELF, ...)
946 SELF->kacc = SvIV(ST(1));
953 paired_account_num(Transaction* SELF, ...)
956 SELF->kxferacc = SvIV(ST(1));
958 RETVAL = SELF->kxferacc;
963 date(Transaction* SELF, ...)
966 SELF->date = SvIV(ST(1));
968 if (GIMME_V == G_ARRAY) {
969 GDate* d = g_date_new_julian(SELF->date);
971 mXPUSHi(g_date_get_day(d));
973 mXPUSHi(g_date_get_month(d));
975 mXPUSHi(g_date_get_year(d));
979 XSRETURN_IV(SELF->date);
983 wording(Transaction* SELF, ...)
986 if (SELF->wording) g_free(SELF->wording);
987 SELF->wording = g_strdup(SvGchar_ptr(ST(1)));
989 RETVAL = SELF->wording ? SELF->wording : "";
994 info(Transaction* SELF, ...)
997 if (SELF->info) g_free(SELF->info);
998 SELF->info = g_strdup(SvGchar_ptr(ST(1)));
1000 RETVAL = SELF->info ? SELF->info : "";
1005 open(Transaction* SELF)
1007 RETVAL = G_OBJECT(create_deftransaction_window(NULL, TRANSACTION_EDIT_MODIFY, FALSE));
1008 deftransaction_set_transaction(GTK_WIDGET(RETVAL), SELF);
1013 pair_with(Transaction* SELF, Transaction* other, ...)
1019 list = g_list_append(list, other);
1020 for (i = 2; i < items; ++i) {
1021 Transaction* ptr = NULL;
1023 EXT_P2C_OBJECT("HomeBank::Transaction", sv, ptr, Transaction*);
1024 list = g_list_append(list, ptr);
1026 other = ui_dialog_transaction_xfer_select_child(SELF, list);
1029 transaction_xfer_change_to_child(SELF, other);
1030 SELF->paymode = PAYMODE_INTXFER;
1039 dump(Transaction* SELF)
1041 g_print("txn: %p (%s) at %u (%d/%d) flags:%d, paymode:%d, kpay:%d, kcat:%d", SELF,
1042 SELF->wording, SELF->date, SELF->kacc, SELF->kxferacc, SELF->flags, SELF->paymode, SELF->kpay, SELF->kcat);