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