Oh no, it worketh!
[cacao.git] / src / vmcore / suck.c
1 /* src/vmcore/suck.c - functions to read LE ordered types from a buffer
2
3    Copyright (C) 1996-2005, 2006, 2007, 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #include "config.h"
27
28 #include <assert.h>
29 #include <stdlib.h>
30
31 #include "vm/types.h"
32
33 #include "arch.h"
34
35 #include "mm/memory.h"
36
37 #include "threads/lock-common.h"
38
39 #include "toolbox/list.h"
40 #include "toolbox/logging.h"
41 #include "toolbox/util.h"
42
43 #include "vm/exceptions.h"
44 #include "vm/properties.h"
45 #include "vm/vm.h"
46
47 #include "vmcore/loader.h"
48 #include "vmcore/options.h"
49 #include "vmcore/suck.h"
50 #include "vmcore/system.h"
51 #include "vmcore/zip.h"
52
53
54 /* global variables ***********************************************************/
55
56 list_t *list_classpath_entries;
57
58
59 /* suck_init *******************************************************************
60
61    Initializes the suck subsystem like initializing the classpath
62    entries list.
63
64 *******************************************************************************/
65
66 bool suck_init(void)
67 {
68         TRACESUBSYSTEMINITIALIZATION("suck_init");
69
70         list_classpath_entries = list_create(OFFSET(list_classpath_entry, linkage));
71
72         /* everything's ok */
73
74         return true;
75 }
76
77
78 /* scandir_filter **************************************************************
79
80    Filters for zip/jar files.
81
82 *******************************************************************************/
83
84 #if defined(__LINUX__)
85 static int scandir_filter(const struct dirent *a)
86 #else
87 static int scandir_filter(struct dirent *a)
88 #endif
89 {
90         s4 namlen;
91
92 #if defined(_DIRENT_HAVE_D_NAMLEN)
93         namlen = a->d_namlen;
94 #else
95         namlen = strlen(a->d_name);
96 #endif
97
98         if ((strncasecmp(a->d_name + namlen - 4, ".zip", 4) == 0) ||
99                 (strncasecmp(a->d_name + namlen - 4, ".jar", 4) == 0))
100                 return 1;
101
102         return 0;
103 }
104
105
106 /* suck_add ********************************************************************
107
108    Adds a classpath to the global classpath entries list.
109
110 *******************************************************************************/
111
112 void suck_add(char *classpath)
113 {
114         list_classpath_entry *lce;
115         char                 *start;
116         char                 *end;
117         char                 *filename;
118         s4                    filenamelen;
119         bool                  is_zip;
120         char                 *cwd;
121         s4                    cwdlen;
122 #if defined(ENABLE_ZLIB)
123         hashtable            *ht;
124 #endif
125
126         /* parse the classpath string */
127
128         for (start = classpath; (*start) != '\0'; ) {
129
130                 /* search for ':' delimiter to get the end of the current entry */
131                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
132
133                 if (start != end) {
134                         is_zip = false;
135                         filenamelen = end - start;
136
137                         if (filenamelen > 4) {
138                                 if ((strncasecmp(end - 4, ".zip", 4) == 0) ||
139                                         (strncasecmp(end - 4, ".jar", 4) == 0)) {
140                                         is_zip = true;
141                                 }
142                         }
143
144                         /* save classpath entries as absolute pathnames */
145
146                         cwd = NULL;
147                         cwdlen = 0;
148
149                         if (*start != '/') {                      /* XXX fix me for win32 */
150                                 cwd = _Jv_getcwd();
151                                 cwdlen = strlen(cwd) + strlen("/");
152                         }
153
154                         /* allocate memory for filename and fill it */
155
156                         filename = MNEW(char, filenamelen + cwdlen + strlen("/") +
157                                                         strlen("0"));
158
159                         if (cwd) {
160                                 strcpy(filename, cwd);
161                                 strcat(filename, "/");
162                                 strncat(filename, start, filenamelen);
163
164                                 /* add cwd length to file length */
165                                 filenamelen += cwdlen;
166
167                         } else {
168                                 strncpy(filename, start, filenamelen);
169                                 filename[filenamelen] = '\0';
170                         }
171
172                         lce = NULL;
173
174                         if (is_zip) {
175 #if defined(ENABLE_ZLIB)
176                                 ht = zip_open(filename);
177
178                                 if (ht != NULL) {
179                                         lce = NEW(list_classpath_entry);
180
181                                         lce->type      = CLASSPATH_ARCHIVE;
182                                         lce->htclasses = ht;
183                                         lce->path      = filename;
184                                         lce->pathlen   = filenamelen;
185
186                                         /* SUN compatible -verbose:class output */
187
188                                         if (opt_verboseclass)
189                                                 printf("[Opened %s]\n", filename);
190                                 }
191
192 #else
193                                 vm_abort("suck_add: zip/jar files not supported");
194 #endif
195                         }
196                         else {
197                                 if (filename[filenamelen - 1] != '/') {/* XXX fixme for win32 */
198                                         filename[filenamelen] = '/';
199                                         filename[filenamelen + 1] = '\0';
200                                         filenamelen++;
201                                 }
202
203                                 lce = NEW(list_classpath_entry);
204
205                                 lce->type    = CLASSPATH_PATH;
206                                 lce->path    = filename;
207                                 lce->pathlen = filenamelen;
208                         }
209
210                         /* add current classpath entry, if no error */
211
212                         if (lce != NULL)
213                                 list_add_last(list_classpath_entries, lce);
214                 }
215
216                 /* goto next classpath entry, skip ':' delimiter */
217
218                 if ((*end) == ':')
219                         start = end + 1;
220                 else
221                         start = end;
222         }
223 }
224
225
226 /* suck_add_from_property ******************************************************
227
228    Adds a classpath form a property entry to the global classpath
229    entries list.
230
231 *******************************************************************************/
232
233 void suck_add_from_property(char *key)
234 {
235         char           *value;
236         char           *start;
237         char           *end;
238         char           *path;
239         s4              pathlen;
240         struct dirent **namelist;
241         s4              n;
242         s4              i;
243         s4              namlen;
244         char           *boot_class_path;
245         char           *p;
246
247         /* get the property value */
248
249         value = properties_get(key);
250
251         if (value == NULL)
252                 return;
253
254         /* get the directory entries of the property */
255
256         for (start = value; (*start) != '\0'; ) {
257
258                 /* search for ':' delimiter to get the end of the current entry */
259
260                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
261
262                 /* found an entry */
263
264                 if (start != end) {
265                         /* allocate memory for the path entry */
266
267                         pathlen = end - start;
268                         path = MNEW(char, pathlen + strlen("0"));
269
270                         /* copy and terminate the string */
271
272                         strncpy(path, start, pathlen);
273                         path[pathlen] = '\0';
274
275                         /* Reset namelist to NULL for the freeing in an error case
276                            (see below). */
277
278                         namelist = NULL;
279
280                         /* scan the directory found for zip/jar files */
281
282                         n = system_scandir(path, &namelist, scandir_filter, alphasort);
283
284                         /* On error, just continue, this should be ok. */
285
286                         if (n > 0) {
287                                 for (i = 0; i < n; i++) {
288 #if defined(_DIRENT_HAVE_D_NAMLEN)
289                                         namlen = namelist[i]->d_namlen;
290 #else
291                                         namlen = strlen(namelist[i]->d_name);
292 #endif
293
294                                         /* Allocate memory for bootclasspath. */
295
296                                         boot_class_path = properties_get("sun.boot.class.path");
297
298                                         p = MNEW(char,
299                                                          pathlen + strlen("/") + namlen +
300                                                          strlen(":") +
301                                                          strlen(boot_class_path) +
302                                                          strlen("0"));
303
304                                         /* Prepend the file found to the bootclasspath. */
305
306                                         strcpy(p, path);
307                                         strcat(p, "/");
308                                         strcat(p, namelist[i]->d_name);
309                                         strcat(p, ":");
310                                         strcat(p, boot_class_path);
311
312                                         properties_add("sun.boot.class.path", p);
313                                         properties_add("java.boot.class.path", p);
314
315                                         MFREE(boot_class_path, char, strlen(boot_class_path));
316
317                                         /* free the memory allocated by scandir */
318                                         /* (We use `free` as the memory came from the C library.) */
319
320                                         free(namelist[i]);
321                                 }
322                         }
323
324                         /* On some systems (like Linux) when n == 0, then namelist
325                            returned from scnadir is NULL, thus we don't have to
326                            free it.
327                            (Use `free` as the memory came from the C library.) */
328
329                         if (namelist != NULL)
330                                 free(namelist);
331
332                         MFREE(path, char, pathlen + strlen("0"));
333                 }
334
335                 /* goto next entry, skip ':' delimiter */
336
337                 if ((*end) == ':')
338                         start = end + 1;
339                 else
340                         start = end;
341         }
342 }
343
344
345 /* suck_check_classbuffer_size *************************************************
346
347    Assert that at least <len> bytes are left to read <len> is limited
348    to the range of non-negative s4 values.
349
350 *******************************************************************************/
351
352 bool suck_check_classbuffer_size(classbuffer *cb, s4 len)
353 {
354 #ifdef ENABLE_VERIFIER
355         if (len < 0 || ((cb->data + cb->size) - cb->pos) < len) {
356                 exceptions_throw_classformaterror((cb)->class, "Truncated class file");
357                 return false;
358         }
359 #endif /* ENABLE_VERIFIER */
360
361         return true;
362 }
363
364
365 u1 suck_u1(classbuffer *cb)
366 {
367         u1 a;
368
369         a = SUCK_BE_U1(cb->pos);
370         cb->pos++;
371
372         return a;
373 }
374
375
376 u2 suck_u2(classbuffer *cb)
377 {
378         u2 a;
379
380         a = SUCK_BE_U2(cb->pos);
381         cb->pos += 2;
382
383         return a;
384 }
385
386
387 u4 suck_u4(classbuffer *cb)
388 {
389         u4 a;
390
391         a = SUCK_BE_U4(cb->pos);
392         cb->pos += 4;
393
394         return a;
395 }
396
397
398 u8 suck_u8(classbuffer *cb)
399 {
400 #if U8_AVAILABLE == 1
401         u8 a;
402
403         a = SUCK_BE_U8(cb->pos);
404         cb->pos += 8;
405
406         return a;
407 #else
408         u8 v;
409
410         v.high = suck_u4(cb);
411         v.low = suck_u4(cb);
412
413         return v;
414 #endif
415 }
416
417
418 float suck_float(classbuffer *cb)
419 {
420         float f;
421
422 #if WORDS_BIGENDIAN == 0
423         u1 buffer[4];
424         u2 i;
425
426         for (i = 0; i < 4; i++)
427                 buffer[3 - i] = suck_u1(cb);
428
429         MCOPY((u1 *) (&f), buffer, u1, 4);
430 #else
431         suck_nbytes((u1*) (&f), cb, 4);
432 #endif
433
434         assert(sizeof(float) == 4);
435         
436         return f;
437 }
438
439
440 double suck_double(classbuffer *cb)
441 {
442         double d;
443
444 #if WORDS_BIGENDIAN == 0
445         u1 buffer[8];
446         u2 i;   
447
448 # if defined(__ARM__) && defined(__ARMEL__) && !defined(__VFP_FP__)
449         /*
450          * On little endian ARM processors when using FPA, word order
451          * of doubles is still big endian. So take that into account
452          * here. When using VFP, word order of doubles follows byte
453          * order. (michi 2005/07/24)
454          */
455         for (i = 0; i < 4; i++)
456                 buffer[3 - i] = suck_u1(cb);
457         for (i = 0; i < 4; i++)
458                 buffer[7 - i] = suck_u1(cb);
459 # else
460         for (i = 0; i < 8; i++)
461                 buffer[7 - i] = suck_u1(cb);
462 # endif /* defined(__ARM__) && ... */
463
464         MCOPY((u1 *) (&d), buffer, u1, 8);
465 #else 
466         suck_nbytes((u1*) (&d), cb, 8);
467 #endif
468
469         assert(sizeof(double) == 8);
470         
471         return d;
472 }
473
474
475 /* suck_nbytes *****************************************************************
476
477    Transfer block of classfile data into a buffer.
478
479 *******************************************************************************/
480
481 void suck_nbytes(u1 *buffer, classbuffer *cb, s4 len)
482 {
483         MCOPY(buffer, cb->pos, u1, len);
484         cb->pos += len;
485 }
486
487
488 /* suck_skip_nbytes ************************************************************
489
490    Skip block of classfile data.
491
492 *******************************************************************************/
493
494 void suck_skip_nbytes(classbuffer *cb, s4 len)
495 {
496         cb->pos += len;
497 }
498
499
500 /* suck_start ******************************************************************
501
502    Returns true if classbuffer is already loaded or a file for the
503    specified class has succussfully been read in. All directories of
504    the searchpath are used to find the classfile (<classname>.class).
505    Returns NULL if no classfile is found and writes an error message.
506         
507 *******************************************************************************/
508
509 classbuffer *suck_start(classinfo *c)
510 {
511         list_classpath_entry *lce;
512         char                 *filename;
513         s4                    filenamelen;
514         char                 *path;
515         FILE                 *classfile;
516         s4                    len;
517         struct stat           buffer;
518         classbuffer          *cb;
519
520         /* initialize return value */
521
522         cb = NULL;
523
524         /* get the classname as char string (do it here for the warning at
525        the end of the function) */
526
527         filenamelen = utf_bytes(c->name) + strlen(".class") + strlen("0");
528         filename = MNEW(char, filenamelen);
529
530         utf_copy(filename, c->name);
531         strcat(filename, ".class");
532
533         /* walk through all classpath entries */
534
535         for (lce = list_first(list_classpath_entries); lce != NULL && cb == NULL;
536                  lce = list_next(list_classpath_entries, lce)) {
537 #if defined(ENABLE_ZLIB)
538                 if (lce->type == CLASSPATH_ARCHIVE) {
539
540                         /* enter a monitor on zip/jar archives */
541
542                         LOCK_MONITOR_ENTER(lce);
543
544                         /* try to get the file in current archive */
545
546                         cb = zip_get(lce, c);
547
548                         /* leave the monitor */
549
550                         LOCK_MONITOR_EXIT(lce);
551
552                 } else {
553 #endif /* defined(ENABLE_ZLIB) */
554                         path = MNEW(char, lce->pathlen + filenamelen);
555                         strcpy(path, lce->path);
556                         strcat(path, filename);
557
558                         classfile = system_fopen(path, "r");
559
560                         if (classfile) {                                   /* file exists */
561                                 if (!system_stat(path, &buffer)) {     /* read classfile data */
562                                         cb = NEW(classbuffer);
563                                         cb->class = c;
564                                         cb->size  = buffer.st_size;
565                                         cb->data  = MNEW(u1, cb->size);
566                                         cb->pos   = cb->data;
567                                         cb->path  = lce->path;
568
569                                         /* read class data */
570
571                                         len = system_fread((void *) cb->data, 1, cb->size,
572                                                                            classfile);
573
574                                         if (len != buffer.st_size) {
575                                                 suck_stop(cb);
576 /*                                              if (ferror(classfile)) { */
577 /*                                              } */
578                                         }
579
580                                         /* close the class file */
581
582                                         system_fclose(classfile);
583                                 }
584                         }
585
586                         MFREE(path, char, lce->pathlen + filenamelen);
587 #if defined(ENABLE_ZLIB)
588                 }
589 #endif
590         }
591
592         if (opt_verbose)
593                 if (cb == NULL)
594                         dolog("Warning: Can not open class file '%s'", filename);
595
596         MFREE(filename, char, filenamelen);
597
598         return cb;
599 }
600
601
602 /* suck_stop *******************************************************************
603
604    Frees memory for buffer with classfile data.
605
606    CAUTION: This function may only be called if buffer has been
607    allocated by suck_start with reading a file.
608         
609 *******************************************************************************/
610
611 void suck_stop(classbuffer *cb)
612 {
613         /* free memory */
614
615         MFREE(cb->data, u1, cb->size);
616         FREE(cb, classbuffer);
617 }
618
619
620 /*
621  * These are local overrides for various environment variables in Emacs.
622  * Please do not remove this and leave it at the end of the file, where
623  * Emacs will automagically detect them.
624  * ---------------------------------------------------------------------
625  * Local variables:
626  * mode: c
627  * indent-tabs-mode: t
628  * c-basic-offset: 4
629  * tab-width: 4
630  * End:
631  */