]> git.draconx.ca Git - upkg.git/blob - src/libupkg.c
Move stuff into a separate source directory.
[upkg.git] / src / 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                         len += 4;
138
139                         nbuf -= len + 1;
140                         memmove(buf, buf+len+1, nbuf);
141                         index++;
142                 } else {
143                         char *c = memchr(buf, 0, nbuf);
144                         if (!c || nbuf <= c - buf + 5)
145                                 goto err;
146                         len = c - buf + 1;
147                         name->name = malloc(len);
148                         if (!name->name)
149                                 goto err;
150                         memcpy(name->name, buf, len);
151                         name->flags = unpack_32_le(buf+len);
152                         len += 4;
153
154                         nbuf -= len;
155                         memmove(buf, buf+len, nbuf);
156                         index++;
157                 }
158         }
159
160         return 0;
161 err:
162         for (unsigned i = 0; i < index; i++)
163                 free(pkg->priv->names[i].name);
164         free(pkg->priv->names);
165         return -1;
166 }
167
168 static int pkg_init_exports(struct upkg *pkg)
169 {
170         size_t rc, len, nbuf = 0;
171         unsigned long index = 0;
172         char buf[512];
173
174         if (fseek(pkg->priv->f, pkg->priv->export_offset, SEEK_SET) != 0)
175                 return -1;
176
177         pkg->priv->exports = malloc(pkg->export_count * sizeof *pkg->priv->exports);
178         if (!pkg->priv->exports)
179                 return -1;
180
181         while (index < pkg->export_count) {
182                 struct upkg_export *export = &pkg->priv->exports[index];
183                 long tmp;
184
185                 /* Read some data into buffer. */
186                 if (!feof(pkg->priv->f)) {
187                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
188                         if (rc == 0)
189                                 goto err;
190                         nbuf += rc;
191                 }
192
193                 len = 0;
194                 rc = decode_index(&export->class, buf+len, nbuf-len);
195                 if (rc == 0) goto err;
196                 len += rc;
197
198                 rc = decode_index(&export->super, buf+len, nbuf-len);
199                 if (rc == 0) goto err;
200                 len += rc;
201
202                 if (nbuf-len < 4) goto err;
203                 export->package = unpack_32_le(buf+len);
204                 len += 4;
205
206                 rc = decode_index(&tmp, buf+len, nbuf-len);
207                 if (rc == 0 || tmp < 0 || tmp >= pkg->name_count) goto err;
208                 export->name = pkg->priv->names[tmp].name;
209                 len += rc;
210
211                 if (nbuf-len < 4) goto err;
212                 export->flags = unpack_32_le(buf+len);
213                 len += 4;
214
215                 rc = decode_index(&export->size, buf+len, nbuf-len);
216                 if (rc == 0) goto err;
217                 len += rc;
218
219                 if (export->size) {
220                         rc = decode_index(&export->offset, buf+len, nbuf-len);
221                         if (rc == 0) goto err;
222                         len += rc;
223                 }
224
225                 nbuf -= len;
226                 memmove(buf, buf+len, nbuf);
227                 index++;
228         }
229
230         return 0;
231 err:
232         free(pkg->priv->exports);
233         return -1;
234 }
235
236 static int pkg_init_imports(struct upkg *pkg)
237 {
238         size_t rc, len, nbuf = 0;
239         unsigned long index = 0;
240         char buf[512];
241
242         if (fseek(pkg->priv->f, pkg->priv->import_offset, SEEK_SET) != 0)
243                 return -1;
244
245         pkg->priv->imports = malloc(pkg->import_count * sizeof *pkg->priv->imports);
246         if (!pkg->priv->imports)
247                 return -1;
248
249         while (index < pkg->import_count) {
250                 struct upkg_import *import = &pkg->priv->imports[index];
251                 long tmp;
252
253                 /* Read some data into buffer. */
254                 if (!feof(pkg->priv->f)) {
255                         rc = fread(buf+nbuf, 1, sizeof buf-nbuf, pkg->priv->f);
256                         if (rc == 0)
257                                 goto err;
258                         nbuf += rc;
259                 }
260
261                 len = 0;
262                 rc = decode_index(&tmp, buf+len, nbuf-len);
263                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
264                 import->class_package = pkg->priv->names[tmp].name;
265                 len += rc;
266
267                 rc = decode_index(&tmp, buf+len, nbuf-len);
268                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
269                 import->class_name = pkg->priv->names[tmp].name;
270                 len += rc;
271
272                 if (nbuf-len < 4) goto err;
273                 import->package = unpack_32_le(buf+len);
274                 len += 4;
275
276                 rc = decode_index(&tmp, buf+len, nbuf-len);
277                 if (rc == 0 || len < 0 || len >= pkg->name_count) goto err;
278                 import->object_name = pkg->priv->names[tmp].name;
279                 len += rc;
280
281                 nbuf -= len;
282                 memmove(buf, buf+len, nbuf);
283                 index++;
284         }
285
286         return 0;
287 err:
288         free(pkg->priv->imports);
289         return -1;
290 }
291
292 struct upkg *upkg_fopen(const char *path)
293 {
294         unsigned char hdr_buf[UPKG_HDR_SIZE];
295         struct upkg *pkg;
296         FILE *f;
297
298         if (!(f = fopen(path, "rb"))) {
299                 return NULL;
300         }
301         if (fread(hdr_buf, sizeof hdr_buf, 1, f) != 1) {
302                 goto err1;
303         }
304         if (unpack_32_le(hdr_buf) != UPKG_HDR_MAGIC) {
305                 goto err1;
306         }
307
308         /* Initialize package structure. */
309         pkg = init_upkg(hdr_buf);
310         if (!pkg) {
311                 goto err1;
312         }
313         pkg->priv->f = f;
314
315         if (pkg_init_names(pkg) != 0) {
316                 goto err2;
317         }
318
319         if (pkg_init_exports(pkg) != 0) {
320                 goto err3;
321         }
322
323         if (pkg_init_imports(pkg) != 0) {
324                 goto err4;
325         }
326
327         return pkg;
328 err4:
329         free(pkg->priv->exports);
330 err3:
331         for (unsigned i = 0; i < pkg->name_count; i++)
332                 free(pkg->priv->names[i].name);
333         free(pkg->priv->names);
334 err2:
335         free(pkg->priv);
336         free(pkg);
337 err1:
338         fclose(f);
339         return NULL;
340 }
341
342 int upkg_close(struct upkg *pkg)
343 {
344         int rc = 0;
345
346         if (pkg->priv->f) {
347                 rc = fclose(pkg->priv->f);
348
349                 for (unsigned i = 0; i < pkg->name_count; i++) {
350                         free(pkg->priv->names[i].name);
351                 }
352         }
353
354         free(pkg->priv->imports);
355         free(pkg->priv->exports);
356         free(pkg->priv->names);
357         free(pkg->priv);
358         free(pkg);
359
360         return rc;
361 }
362
363 const char *upkg_get_name(struct upkg *pkg, unsigned long idx)
364 {
365         if (idx >= pkg->name_count)
366                 return 0;
367         return pkg->priv->names[idx].name;
368 }