]> git.draconx.ca Git - upkg.git/blob - src/package.c
upkg: Update verbosity behaviour for --info.
[upkg.git] / src / package.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 3 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, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23
24 #include <glib-object.h>
25 #include <ltdl.h>
26
27 #include <uobject/package.h>
28 #include "upkg.h"
29
30 #define U_PKG_GET_PRIV(o) \
31         G_TYPE_INSTANCE_GET_PRIVATE(o, U_PKG_TYPE, struct upkg_priv)
32
33 struct upkg_priv {
34         lt_dlhandle native;
35 };
36
37 G_DEFINE_TYPE(UPkg, u_pkg, G_TYPE_TYPE_MODULE);
38
39 /* Package search path. */
40 static char  *search_path;
41 static size_t search_path_sz;
42
43 /* List of package file extensions, in descending order of precedence. */
44 static const char u_pkg_exts[][5] = { ".u", ".utx", ".umx", ".uax", ".unr" };
45
46 static char *str_cpy_lower(char *dst, const char *src)
47 {
48         size_t i;
49
50         for (i = 0; src[i]; i++)
51                 dst[i] = tolower(src[i]);
52         dst[i] = 0;
53
54         return dst;
55 }
56
57 static int str_cmp_lower(const char *s1, const char *s2)
58 {
59         size_t i;
60
61         for (i = 0; s1[i] && s2[i]; i++) {
62                 int c1 = tolower(s1[i]), c2 = tolower(s2[i]);
63                 if (c1 < c2)
64                         return -1;
65                 if (c1 > c2)
66                         return 1;
67         }
68
69         if (s1[i])
70                 return 1;
71         if (s2[i])
72                 return -1;
73         return 0;
74 }
75
76 static void dl_print_errors(const char *prefix)
77 {
78         const char *err;
79         while ((err = lt_dlerror())) {
80                 if (prefix) fprintf(stderr, "%s: ", prefix);
81                 fprintf(stderr, "%s\n", err);
82         }
83 }
84
85 /*
86  * Function for use with lt_dlforeachfile.  The user data must point to
87  * a UPkg with the GTypeModule name field initialized.  If a suitable file
88  * is found and could be successfully opened for reading, the pkg class member
89  * will be filled in.
90  */
91 static int find_package_by_name(const char *filename, void *data)
92 {
93         GTypeModule *m = G_TYPE_MODULE(data);
94         UPkg *upkg = U_PKG(data);
95
96         const char *base;
97         size_t len;
98         char *buf;
99
100         base = strrchr(filename, '/');
101         if (base) {
102                 base++;
103         } else {
104                 base = filename;
105         }
106
107         if (str_cmp_lower(base, m->name) != 0)
108                 return 0;
109
110         len = strlen(filename);
111         buf = malloc(len + sizeof **u_pkg_exts);
112         if (!buf)
113                 return 0;
114         strcpy(buf, filename);
115
116         for (unsigned i = 0; i < sizeof u_pkg_exts / sizeof *u_pkg_exts; i++) {
117                 strcpy(buf+len, u_pkg_exts[i]);
118                 upkg->pkg = upkg_fopen(buf);
119                 if (!upkg->pkg) {
120                         free(buf);
121                         return 1;
122                 }
123         }
124
125         free(buf);
126         return 0;
127 }
128
129 static gboolean u_pkg_load(GTypeModule *m)
130 {
131         struct upkg_priv *priv = U_PKG_GET_PRIV(m);
132         int (*init_func)(GTypeModule *);
133
134         if (m->name) {
135                 priv->native = lt_dlopenext(m->name);
136                 if (!priv->native) {
137                         dl_print_errors(m->name);
138                         return FALSE;
139                 }
140
141                 init_func = lt_dlsym(priv->native, "init");
142                 if (!init_func || init_func(m) != 0) {
143                         dl_print_errors(__func__);
144                         lt_dlclose(priv->native);
145                         return FALSE;
146                 }
147         }
148
149         if (!U_PKG(m)->pkg) {
150                 lt_dlforeachfile(u_pkg_get_search_path(), find_package_by_name, m);
151         }
152
153         return TRUE;
154 }
155
156 static void u_pkg_unload(GTypeModule *m)
157 {
158         struct upkg_priv *priv = U_PKG_GET_PRIV(m);
159         void (*exit_func)(GTypeModule *);
160         UPkg *upkg = U_PKG(m);
161
162         if (priv->native) {
163                 exit_func = lt_dlsym(priv->native, "exit");
164                 if (exit_func) {
165                         exit_func(m);
166                 }
167
168                 if (lt_dlclose(priv->native) != 0) {
169                         dl_print_errors(__func__);
170                 }
171         }
172
173         if (upkg->pkg) {
174                 upkg_close(upkg->pkg);
175                 upkg->pkg = NULL;
176         }
177 }
178
179 static void u_pkg_init(UPkg *pkg)
180 {
181
182 }
183
184 static void u_pkg_finalize(GObject *o)
185 {
186         UPkg *upkg = U_PKG(o);
187
188         if (upkg->pkg) {
189                 upkg_close(upkg->pkg);
190         }
191 }
192
193 static void u_pkg_class_init(UPkgClass *class)
194 {
195         GTypeModuleClass *modclass = G_TYPE_MODULE_CLASS(class);
196         GObjectClass *objclass = G_OBJECT_CLASS(class);
197
198         const char *modpath = getenv("UOBJECT_MODULE_PATH");
199
200         g_type_class_add_private(class, sizeof (struct upkg_priv));
201
202         if (lt_dlinit() != 0) {
203                 dl_print_errors(__func__);
204         }
205
206         if (modpath && lt_dlsetsearchpath(modpath) != 0) {
207                 dl_print_errors(__func__);
208         }
209         if (lt_dladdsearchdir(PKGLIBDIR) != 0) {
210                 dl_print_errors(__func__);
211         }
212
213         modclass->load     = u_pkg_load;
214         modclass->unload   = u_pkg_unload;
215         objclass->finalize = u_pkg_finalize;
216 }
217
218 static int expand_search_path(size_t need)
219 {
220         size_t want = search_path_sz;
221         if (want == 0) want = 1;
222
223         while (want < need)
224                 want *= 2;
225
226         if (want > search_path_sz) {
227                 char *new = realloc(search_path, want);
228                 if (!new) {
229                         return -1;
230                 }
231
232                 search_path    = new;
233                 search_path_sz = want;
234         }
235
236         return 0;
237 }
238
239 const char *u_pkg_get_search_path(void)
240 {
241         return search_path ? search_path : "";
242 }
243
244 int u_pkg_set_search_path(const char *path)
245 {
246         if (expand_search_path(strlen(path)+1) != 0)
247                 return -1;
248         strcpy(search_path, path);
249         return 0;
250 }
251
252 int u_pkg_add_search_dir(const char *path)
253 {
254         size_t end = search_path ? strlen(search_path) : 0;
255
256         if (end == 0) {
257                 return u_pkg_set_search_path(path);
258         }
259
260         if (expand_search_path(end + strlen(path) + 2) != 0)
261                 return -1;
262         search_path[end] = LT_PATHSEP_CHAR;
263         strcpy(search_path+end+1, path);
264         return 0;
265 }
266
267 GTypeModule *u_pkg_new_by_name(const char *name)
268 {
269         g_return_val_if_fail(name != NULL, NULL);
270
271         char *pkgname = malloc(strlen(name)+1);
272         if (!pkgname) {
273                 return NULL;
274         }
275
276         GTypeModule *mod = g_object_new(U_PKG_TYPE, NULL);
277         if (!mod) {
278                 free(pkgname);
279                 return NULL;
280         }
281
282         mod->name = str_cpy_lower(pkgname, name);
283         return mod;
284 }
285
286 GTypeModule *u_pkg_new_by_file(const char *filename)
287 {
288         struct upkg *pkg = upkg_fopen(filename);
289         if (!pkg) {
290                 return NULL;
291         }
292
293         GTypeModule *mod = g_object_new(U_PKG_TYPE, NULL);
294         if (!mod) {
295                 upkg_close(pkg);
296                 return NULL;
297         }
298
299         mod->name = NULL;
300         U_PKG(mod)->pkg = pkg;
301         return mod;
302 }