]> git.draconx.ca Git - upkg.git/blob - libupkg.c
85767a2e4262e96be5984655c99f8d1a338dfbb4
[upkg.git] / libupkg.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "upkg.h"
6 #include "pack.h"
7
8 #define MIN(a, b) ((a) < (b) ? (a) : (b))
9
10 struct upkg_name {
11         unsigned long flags;
12         char *name;
13 };
14
15 struct upkg_export {
16         const char *name;
17
18         long package, class, super;
19         unsigned long flags;
20         unsigned long size, offset;
21 };
22
23 struct upkg_import {
24         const char *class_package, *class_name, *object_name;
25         long package;
26 };
27
28 struct upkg_private {
29         FILE *f;
30
31         struct upkg_name   *names;
32         struct upkg_export *exports;
33         struct upkg_import *imports;
34
35         unsigned long name_offset, export_offset, import_offset;
36         unsigned char guid[16];
37 };
38
39 /*
40  * Decode the compact index format from the upkg.  This format is fucked.
41  * Stores the result in *val and returns the number of input bytes read (or 0
42  * if the input is invalid, in which case *val is undefined).
43  */
44 static size_t decode_index(long *val, unsigned char *bytes, size_t n)
45 {
46         size_t i = 0;
47
48         *val = 0;
49         while (i < MIN(n, 5)) {
50                 /*
51                  * Least significant bytes are first, so we need to do this
52                  * nonsense.
53                  */
54                 long tmp = bytes[i] & (i == 0 ? 0x3f : 0x7f);
55
56                 if (i > 0) tmp <<= 6;
57                 if (i > 1) tmp <<= 7*(i-1);
58                 *val += tmp;
59
60                 if (!(bytes[i] & (i == 0 ? 0x40 : 0x80))) {
61                         i++;
62                         break;
63                 }
64
65                 i++;
66         }
67
68         if (i > MIN(n, 5) || n == 0)
69                 return 0;
70         if (bytes[0] & 0x80)
71                 *val = -*val;
72         return i;
73 }
74
75 static struct upkg *init_upkg(unsigned char hdr[static UPKG_HDR_SIZE])
76 {
77         struct upkg *pkg;
78
79         pkg = malloc(sizeof *pkg);
80         if (!pkg) {
81                 return NULL;
82         }
83
84         pkg->priv = malloc(sizeof *pkg->priv);
85         if (!pkg->priv) {
86                 free(pkg);
87                 return NULL;
88         }
89
90         pkg->version      = unpack_16_le(hdr+4);
91         pkg->license      = unpack_16_le(hdr+6);
92         pkg->flags        = unpack_32_le(hdr+8);
93         pkg->name_count   = unpack_32_le(hdr+12);
94         pkg->export_count = unpack_32_le(hdr+20);
95         pkg->import_count = unpack_32_le(hdr+28);
96
97         pkg->priv->name_offset   = unpack_32_le(hdr+16);
98         pkg->priv->export_offset = unpack_32_le(hdr+24);
99         pkg->priv->import_offset = unpack_32_le(hdr+32);
100
101         return pkg;
102 }
103
104 static int pkg_init_names(struct upkg *pkg)
105 {
106         size_t rc, len, nbuf = 0;
107         unsigned long index = 0;
108         char buf[512];
109
110         if (fseek(pkg->priv->f, pkg->priv->name_offset, SEEK_SET) != 0)
111                 return -1;
112
113         pkg->priv->names = malloc(pkg->name_count * sizeof *pkg->priv->names);
114         if (!pkg->priv->names)
115                 return -1;
116
117         while (index < pkg->name_count) {
118                 struct upkg_name *name = &pkg->priv->names[index];
119
120                 /* Read some data into buffer. */
121                 if (!feof(pkg->priv->f)) {
122                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
123                         if (rc == 0)
124                                 goto err;
125                         nbuf += rc;
126                 }
127
128                 if (pkg->version >= 64) {
129                         len = buf[0];
130                         if (nbuf <= len + 4 || buf[len])
131                                 goto err;
132                         name->name = malloc(len);
133                         if (!name->name)
134                                 goto err;
135                         memcpy(name->name, buf+1, len);
136                         name->flags = unpack_32_le(buf+len+1);
137
138                         nbuf -= len + 5;
139                         memmove(buf, buf+len+5, nbuf);
140                         index++;
141                 } else {
142                         /* TODO */
143                         abort();
144                 }
145         }
146
147         return 0;
148 err:
149         for (unsigned i = 0; i < index; i++)
150                 free(pkg->priv->names[i].name);
151         free(pkg->priv->names);
152         return -1;
153 }
154
155 static int pkg_init_exports(struct upkg *pkg)
156 {
157         size_t rc, len, nbuf = 0;
158         unsigned long index = 0;
159         char buf[512];
160
161         if (fseek(pkg->priv->f, pkg->priv->export_offset, SEEK_SET) != 0)
162                 return -1;
163
164         pkg->priv->exports = malloc(pkg->export_count * sizeof *pkg->priv->exports);
165         if (!pkg->priv->exports)
166                 return -1;
167
168         while (index < pkg->export_count) {
169                 struct upkg_export *export = &pkg->priv->exports[index];
170                 long tmp;
171
172                 /* Read some data into buffer. */
173                 if (!feof(pkg->priv->f)) {
174                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
175                         if (rc == 0)
176                                 goto err;
177                         nbuf += rc;
178                 }
179
180                 len = 0;
181                 rc = decode_index(&export->class, buf+len, nbuf-len);
182                 if (rc == 0) goto err;
183                 len += rc;
184
185                 rc = decode_index(&export->super, buf+len, nbuf-len);
186                 if (rc == 0) goto err;
187                 len += rc;
188
189                 if (nbuf-len < 4) goto err;
190                 export->package = unpack_32_le(buf+len);
191                 len += 4;
192
193                 rc = decode_index(&tmp, buf+len, nbuf-len);
194                 if (rc == 0 || tmp < 0 || tmp >= pkg->name_count) goto err;
195                 export->name = pkg->priv->names[tmp].name;
196                 len += rc;
197
198                 if (nbuf-len < 4) goto err;
199                 export->flags = unpack_32_le(buf+len);
200                 len += 4;
201
202                 rc = decode_index(&export->size, buf+len, nbuf-len);
203                 if (rc == 0) goto err;
204                 len += rc;
205
206                 if (export->size) {
207                         rc = decode_index(&export->offset, buf+len, nbuf-len);
208                         if (rc == 0) goto err;
209                         len += rc;
210                 }
211
212                 nbuf -= len;
213                 memmove(buf, buf+len, nbuf);
214                 index++;
215         }
216
217         return 0;
218 err:
219         free(pkg->priv->exports);
220         return -1;
221 }
222
223 static int pkg_init_imports(struct upkg *pkg)
224 {
225         size_t rc, len, nbuf = 0;
226         unsigned long index = 0;
227         char buf[512];
228
229         if (fseek(pkg->priv->f, pkg->priv->import_offset, SEEK_SET) != 0)
230                 return -1;
231
232         pkg->priv->imports = malloc(pkg->import_count * sizeof *pkg->priv->imports);
233         if (!pkg->priv->imports)
234                 return -1;
235
236         while (index < pkg->import_count) {
237                 struct upkg_import *import = &pkg->priv->imports[index];
238                 long tmp;
239
240                 /* Read some data into buffer. */
241                 if (!feof(pkg->priv->f)) {
242                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
243                         if (rc == 0)
244                                 goto err;
245                         nbuf += rc;
246                 }
247
248                 len = 0;
249                 rc = decode_index(&tmp, buf+len, nbuf-len);
250                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
251                 import->class_package = pkg->priv->names[tmp].name;
252                 len += rc;
253
254                 rc = decode_index(&tmp, buf+len, nbuf-len);
255                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
256                 import->class_name = pkg->priv->names[tmp].name;
257                 len += rc;
258
259                 if (nbuf-len < 4) goto err;
260                 import->package = unpack_32_le(buf+len);
261                 len += 4;
262
263                 rc = decode_index(&tmp, buf+len, nbuf-len);
264                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
265                 import->object_name = pkg->priv->names[tmp].name;
266                 len += rc;
267
268                 nbuf -= len;
269                 memmove(buf, buf+len, nbuf);
270                 index++;
271         }
272
273         return 0;
274 err:
275         free(pkg->priv->imports);
276         return -1;
277 }
278
279 struct upkg *upkg_fopen(const char *path)
280 {
281         unsigned char hdr_buf[UPKG_HDR_SIZE];
282         struct upkg *pkg;
283         FILE *f;
284
285         if (!(f = fopen(path, "rb"))) {
286                 return NULL;
287         }
288         if (fread(hdr_buf, sizeof hdr_buf, 1, f) != 1) {
289                 goto err1;
290         }
291         if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
292                 goto err1;
293         }
294
295         /* Initialize package structure. */
296         pkg = init_upkg(hdr_buf);
297         if (!pkg) {
298                 goto err1;
299         }
300         pkg->priv->f = f;
301
302         if (pkg_init_names(pkg) != 0) {
303                 goto err2;
304         }
305
306         if (pkg_init_exports(pkg) != 0) {
307                 goto err3;
308         }
309
310         if (pkg_init_imports(pkg) != 0) {
311                 goto err4;
312         }
313
314         return pkg;
315 err4:
316         free(pkg->priv->exports);
317 err3:
318         for (unsigned i = 0; i < pkg->name_count; i++)
319                 free(pkg->priv->names[i].name);
320         free(pkg->priv->names);
321 err2:
322         free(pkg->priv);
323         free(pkg);
324 err1:
325         fclose(f);
326         return NULL;
327 }
328
329 int upkg_close(struct upkg *pkg)
330 {
331         int rc = 0;
332
333         if (pkg->priv->f) {
334                 rc = fclose(pkg->priv->f);
335
336                 for (unsigned i = 0; i < pkg->name_count; i++) {
337                         free(pkg->priv->names[i].name);
338                 }
339         }
340
341         free(pkg->priv->imports);
342         free(pkg->priv->exports);
343         free(pkg->priv->names);
344         free(pkg->priv);
345         free(pkg);
346
347         return rc;
348 }
349
350 const char *upkg_get_name(struct upkg *pkg, unsigned long idx)
351 {
352         if (idx >= pkg->name_count)
353                 return 0;
354         return pkg->priv->names[idx].name;
355 }