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