]> git.draconx.ca Git - upkg.git/blob - src/module.c
uobject: Clean up the module loader namespace.
[upkg.git] / src / module.c
1 /*
2  *  upkg: tool for manipulating Unreal Tournament packages.
3  *  Copyright (C) 2009 Nick Bowler
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <glib-object.h>
25 #include <ltdl.h>
26
27 #include <uobject/module.h>
28 #include "module.h"
29 #include "avl.h"
30
31 static unsigned initialized;
32 static struct avl_table *package_tree;
33
34 static char *str_cpy_lower(char *dst, const char *src)
35 {
36         size_t i;
37
38         for (i = 0; src[i]; i++)
39                 dst[i] = tolower(src[i]);
40         dst[i] = 0;
41
42         return dst;
43 }
44
45 G_DEFINE_TYPE(UPkgModule, upkg_module, G_TYPE_TYPE_MODULE);
46
47 static void dl_print_errors(const char *prefix)
48 {
49         const char *err;
50         while ((err = lt_dlerror())) {
51                 if (prefix) fprintf(stderr, "%s: ", prefix);
52                 puts(err);
53         }
54 }
55
56 static gboolean module_load(GTypeModule *m)
57 {
58         UPkgModule *mod = UPKG_MODULE(m);
59         int (*init_func)(GTypeModule *);
60
61         mod->dl = lt_dlopenext(G_TYPE_MODULE(m)->name);
62         if (!mod->dl) {
63                 dl_print_errors(G_TYPE_MODULE(m)->name);
64                 return FALSE;
65         }
66
67         init_func = lt_dlsym(mod->dl, "init");
68         if (!init_func || init_func(m) != 0) {
69                 dl_print_errors(__func__);
70                 lt_dlclose(mod->dl);
71                 return FALSE;
72         }
73
74         return TRUE;
75 }
76
77 static void module_unload(GTypeModule *m)
78 {
79         UPkgModule *mod = UPKG_MODULE(m);
80
81         if (lt_dlclose(mod->dl) != 0) {
82                 dl_print_errors(__func__);
83         }
84 }
85
86 static void upkg_module_init(UPkgModule *mod)
87 {
88 }
89
90 static void upkg_module_class_init(UPkgModuleClass *class)
91 {
92         GTypeModuleClass *modclass = G_TYPE_MODULE_CLASS(class);
93
94         modclass->load   = module_load;
95         modclass->unload = module_unload;
96 }
97
98 static UPkgModule *uobject_module_new(const char *name)
99 {
100         char *name2;
101
102         if (!name) {
103                 return NULL;
104         }
105
106         name2 = malloc(strlen(name)+1);
107         if (!name2) {
108                 return NULL;
109         }
110
111         UPkgModule *mod = g_object_new(UPKG_MODULE_TYPE, NULL);
112         if (!mod) {
113                 free(name2);
114                 return NULL;
115         }
116
117         G_TYPE_MODULE(mod)->name = str_cpy_lower(name2, name);
118         return mod;
119 }
120
121 static int modcmp(const void *a, const void *b, void *_data)
122 {
123         const GTypeModule *ma = a;
124         const GTypeModule *mb = b;
125
126         return strcmp(ma->name, mb->name);
127 }
128
129 int uobject_module_init(void)
130 {
131         if (!initialized) {
132                 package_tree = avl_create(modcmp, NULL, NULL);
133                 if (!package_tree) {
134                         fprintf(stderr, "%s: failed to create package tree.\n", __func__);
135                         return -1;
136                 }
137
138                 if (lt_dlinit() != 0) {
139                         avl_destroy(package_tree, NULL);
140                         dl_print_errors(__func__);
141                         return -1;
142                 }
143
144                 g_type_init();
145         }
146
147         initialized++;
148         return 0;
149 }
150
151 int uobject_module_exit(void)
152 {
153         if (--initialized)
154                 return 0;
155
156         if (lt_dlexit() != 0) {
157                 dl_print_errors(__func__);
158                 return -1;
159         }
160
161         return 0;
162 }
163
164 GType uobject_module_get_class(const char *package, const char *class)
165 {
166         char buf[strlen(package) + strlen(class) + 1];
167         GTypeModule search = { .name = str_cpy_lower(buf, package) };
168
169         GTypeModule *mod = avl_find(package_tree, &search);
170         if (!mod) {
171                 void **p;
172
173                 mod = G_TYPE_MODULE(uobject_module_new(package));
174                 if (!mod) {
175                         return 0;
176                 }
177
178                 p = avl_probe(package_tree, mod);
179                 if (!p) {
180                         g_object_unref(mod);
181                         return 0;
182                 }
183         }
184
185         if (!g_type_module_use(mod))
186                 return 0;
187
188         str_cpy_lower(buf+strlen(package), class);
189         buf[0] = toupper(buf[0]);
190         buf[strlen(package)] = toupper(buf[strlen(package)]);
191         return g_type_from_name(buf);
192 }