]> git.draconx.ca Git - upkg.git/blob - src/uobject/package.c
package: Get rid of separate mechanisms for opening packages.
[upkg.git] / src / uobject / 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 <uobject/vfs.h>
29 #include "upkg.h"
30
31 #define U_PKG_GET_PRIV(o) \
32         G_TYPE_INSTANCE_GET_PRIVATE(o, U_PKG_TYPE, struct upkg_priv)
33
34 struct upkg_priv {
35         lt_dlhandle native;
36 };
37
38 G_DEFINE_TYPE(UPkg, u_pkg, G_TYPE_TYPE_MODULE);
39
40 /* Package search path. */
41 static char  *search_path;
42 static size_t search_path_sz;
43
44 /* List of package file extensions, in descending order of precedence. */
45 static const char u_pkg_exts[][5] = { ".u", ".utx", ".umx", ".uax", ".unr" };
46
47 static char *str_cpy_lower(char *dst, const char *src)
48 {
49         size_t i;
50
51         for (i = 0; src[i]; i++)
52                 dst[i] = tolower(src[i]);
53         dst[i] = 0;
54
55         return dst;
56 }
57
58 static int str_cmp_lower(const char *s1, const char *s2)
59 {
60         size_t i;
61
62         for (i = 0; s1[i] && s2[i]; i++) {
63                 int c1 = tolower(s1[i]), c2 = tolower(s2[i]);
64                 if (c1 < c2)
65                         return -1;
66                 if (c1 > c2)
67                         return 1;
68         }
69
70         if (s1[i])
71                 return 1;
72         if (s2[i])
73                 return -1;
74         return 0;
75 }
76
77 static void dl_print_errors(const char *prefix)
78 {
79         const char *err;
80         while ((err = lt_dlerror())) {
81                 if (prefix) fprintf(stderr, "%s: ", prefix);
82                 fprintf(stderr, "%s\n", err);
83         }
84 }
85
86 static gboolean u_pkg_load(GTypeModule *m)
87 {
88         struct upkg_priv *priv = U_PKG_GET_PRIV(m);
89         int (*init_func)(GTypeModule *);
90         const char *file;
91
92         file = u_pkg_vfs_lookup(m->name);
93         if (file) {
94                 U_PKG(m)->pkg = upkg_fopen(file);
95                 if (!U_PKG(m)->pkg) {
96                         return FALSE;
97                 }
98         }
99
100         priv->native = lt_dlopenext(m->name);
101         if (priv->native) {
102                 init_func = lt_dlsym(priv->native, "init");
103                 if (!init_func || init_func(m) != 0) {
104                         dl_print_errors(__func__);
105                         lt_dlclose(priv->native);
106                         upkg_close(U_PKG(m)->pkg);
107                         return FALSE;
108                 }
109         }
110
111         return TRUE;
112 }
113
114 static void u_pkg_unload(GTypeModule *m)
115 {
116         struct upkg_priv *priv = U_PKG_GET_PRIV(m);
117         void (*exit_func)(GTypeModule *);
118         UPkg *upkg = U_PKG(m);
119
120         if (priv->native) {
121                 exit_func = lt_dlsym(priv->native, "exit");
122                 if (exit_func) {
123                         exit_func(m);
124                 }
125
126                 if (lt_dlclose(priv->native) != 0) {
127                         dl_print_errors(__func__);
128                 }
129
130                 priv->native = NULL;
131         }
132
133         if (upkg->pkg) {
134                 upkg_close(upkg->pkg);
135                 upkg->pkg = NULL;
136         }
137 }
138
139 static void u_pkg_init(UPkg *pkg)
140 {
141
142 }
143
144 static void u_pkg_finalize(GObject *o)
145 {
146         u_pkg_unload(G_TYPE_MODULE(o));
147 }
148
149 static void u_pkg_class_init(UPkgClass *class)
150 {
151         GTypeModuleClass *modclass = G_TYPE_MODULE_CLASS(class);
152         GObjectClass *objclass = G_OBJECT_CLASS(class);
153
154         const char *modpath = getenv("UOBJECT_MODULE_PATH");
155
156         g_type_class_add_private(class, sizeof (struct upkg_priv));
157
158         if (lt_dlinit() != 0) {
159                 dl_print_errors(__func__);
160         }
161
162         if (modpath && lt_dlsetsearchpath(modpath) != 0) {
163                 dl_print_errors(__func__);
164         }
165         if (lt_dladdsearchdir(PKGLIBDIR) != 0) {
166                 dl_print_errors(__func__);
167         }
168
169         modclass->load     = u_pkg_load;
170         modclass->unload   = u_pkg_unload;
171         objclass->finalize = u_pkg_finalize;
172 }
173
174 static int expand_search_path(size_t need)
175 {
176         size_t want = search_path_sz;
177         if (want == 0) want = 1;
178
179         while (want < need)
180                 want *= 2;
181
182         if (want > search_path_sz) {
183                 char *new = realloc(search_path, want);
184                 if (!new) {
185                         return -1;
186                 }
187
188                 search_path    = new;
189                 search_path_sz = want;
190         }
191
192         return 0;
193 }
194
195 const char *u_pkg_get_search_path(void)
196 {
197         return search_path ? search_path : "";
198 }
199
200 int u_pkg_set_search_path(const char *path)
201 {
202         if (expand_search_path(strlen(path)+1) != 0)
203                 return -1;
204         strcpy(search_path, path);
205         return 0;
206 }
207
208 int u_pkg_add_search_dir(const char *path)
209 {
210         size_t end = search_path ? strlen(search_path) : 0;
211
212         if (end == 0) {
213                 return u_pkg_set_search_path(path);
214         }
215
216         if (expand_search_path(end + strlen(path) + 2) != 0)
217                 return -1;
218         search_path[end] = LT_PATHSEP_CHAR;
219         strcpy(search_path+end+1, path);
220         return 0;
221 }
222
223 GTypeModule *u_pkg_open(const char *name)
224 {
225         g_return_val_if_fail(name != NULL, NULL);
226
227         char *pkgname = malloc(strlen(name)+1);
228         if (!pkgname) {
229                 return NULL;
230         }
231
232         GTypeModule *mod = g_object_new(U_PKG_TYPE, NULL);
233         if (!mod) {
234                 free(pkgname);
235                 return NULL;
236         }
237
238         mod->name = str_cpy_lower(pkgname, name);
239         return mod;
240 }