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