* Updated header: Added 2006. Changed address of FSF. Changed email
[cacao.git] / src / vm / suck.c
1 /* src/vm/suck.c - functions to read LE ordered types from a buffer
2
3    Copyright (C) 1996-2005, 2006 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    Contact: cacao@cacaojvm.org
26
27    Authors: Christian Thalinger
28
29    Changes:
30
31    $Id: suck.c 4357 2006-01-22 23:33:38Z twisti $
32
33 */
34
35
36 #include "config.h"
37
38 #include <dirent.h>
39 #include <sys/stat.h>
40
41 #include "vm/types.h"
42
43 #include "mm/memory.h"
44 #include "toolbox/list.h"
45 #include "toolbox/logging.h"
46 #include "toolbox/util.h"
47 #include "vm/exceptions.h"
48 #include "vm/loader.h"
49 #include "vm/options.h"
50 #include "vm/properties.h"
51 #include "vm/stringlocal.h"
52 #include "vm/suck.h"
53 #include "vm/zip.h"
54
55
56 /* global variables ***********************************************************/
57
58 char *bootclasspath;                    /* contains the boot classpath        */
59 char *classpath;                        /* contains the classpath             */
60
61 list *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 = NEW(list);
74
75         list_init(list_classpath_entries, OFFSET(list_classpath_entry, linkage));
76
77         /* everything's ok */
78
79         return true;
80 }
81
82
83 /* scandir_filter **************************************************************
84
85    Filters for zip/jar files.
86
87 *******************************************************************************/
88
89 static int scandir_filter(const struct dirent *a)
90 {
91         s4 namlen;
92
93 #if defined(_DIRENT_HAVE_D_NAMLEN)
94         namlen = 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         hashtable            *ht;
124
125         /* parse the classpath string */
126
127         for (start = classpath; (*start) != '\0'; ) {
128
129                 /* search for ':' delimiter to get the end of the current entry */
130                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
131
132                 if (start != end) {
133                         is_zip = false;
134                         filenamelen = end - start;
135
136                         if (filenamelen > 4) {
137                                 if ((strncasecmp(end - 4, ".zip", 4) == 0) ||
138                                         (strncasecmp(end - 4, ".jar", 4) == 0)) {
139                                         is_zip = true;
140                                 }
141                         }
142
143                         /* save classpath entries as absolute pathnames */
144
145                         cwd = NULL;
146                         cwdlen = 0;
147
148                         if (*start != '/') {                      /* XXX fix me for win32 */
149                                 cwd = _Jv_getcwd();
150                                 cwdlen = strlen(cwd) + strlen("/");
151                         }
152
153                         /* allocate memory for filename and fill it */
154
155                         filename = MNEW(char, filenamelen + cwdlen + strlen("/") +
156                                                         strlen("0"));
157
158                         if (cwd) {
159                                 strcpy(filename, cwd);
160                                 strcat(filename, "/");
161                                 strncat(filename, start, filenamelen);
162
163                                 /* add cwd length to file length */
164                                 filenamelen += cwdlen;
165
166                         } else {
167                                 strncpy(filename, start, filenamelen);
168                                 filename[filenamelen] = '\0';
169                         }
170
171                         lce = NULL;
172
173                         if (is_zip) {
174 #if defined(ENABLE_ZLIB)
175                                 ht = zip_open(filename);
176
177                                 if (ht) {
178                                         lce = NEW(list_classpath_entry);
179
180                                         lce->type      = CLASSPATH_ARCHIVE;
181                                         lce->htclasses = ht;
182                                         lce->path      = filename;
183                                         lce->pathlen   = filenamelen;
184
185                                         /* SUN compatible -verbose:class output */
186
187                                         if (opt_verboseclass)
188                                                 printf("[Opened %s]\n", filename);
189                                 }
190
191 #else
192                                 throw_cacao_exception_exit(string_java_lang_InternalError,
193                                                                                    "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)
213                                 list_addlast(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           *tmpbootclasspath;
245
246         /* get the property value */
247
248         value = properties_get(key);
249
250         if (value == NULL)
251                 return;
252
253         /* get the directory entries of the property */
254
255         for (start = value; (*start) != '\0'; ) {
256
257                 /* search for ':' delimiter to get the end of the current entry */
258
259                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
260
261                 /* found an entry */
262
263                 if (start != end) {
264                         /* allocate memory for the path entry */
265
266                         pathlen = end - start;
267                         path = MNEW(char, pathlen + strlen("0"));
268
269                         /* copy and terminate the string */
270
271                         strncpy(path, start, pathlen);
272                         path[pathlen] = '\0';
273
274                         /* scan the directory found for zip/jar files */
275
276                         n = scandir(path, &namelist, scandir_filter, alphasort);
277
278                         /* on error, just continue, this should be ok */
279
280                         if (n >= 0) {
281                                 for (i = 0; i < n; i++) {
282 #if defined(_DIRENT_HAVE_D_NAMLEN)
283                                         namlen = namelist[i]->d_namlen;
284 #else
285                                         namlen = strlen(namelist[i]->d_name);
286 #endif
287
288                                         /* reallocate memory for bootclasspath */
289
290                                         tmpbootclasspath = MNEW(char,
291                                                                                         pathlen + strlen("/") + namlen +
292                                                                                         strlen(":") +
293                                                                                         strlen(bootclasspath) +
294                                                                                         strlen("0"));
295
296                                         /* prepend the file found to bootclasspath */
297
298                                         strcpy(tmpbootclasspath, path);
299                                         strcat(tmpbootclasspath, "/");
300                                         strcat(tmpbootclasspath, namelist[i]->d_name);
301                                         strcat(tmpbootclasspath, ":");
302
303                                         strcat(tmpbootclasspath, bootclasspath);
304
305                                         /* free old bootclasspath memory */
306
307                                         MFREE(bootclasspath, u1, strlen(bootclasspath));
308
309                                         /* and set the new bootclasspath */
310
311                                         bootclasspath = tmpbootclasspath;
312
313                                         /* free the memory allocated by scandir */
314
315                                         FREE(namelist[i], struct dirent);
316                                 }
317
318                                 FREE(namelist, struct dirent);
319                         }
320
321                         MFREE(path, char, pathlen + strlen("0"));
322                 }
323
324                 /* goto next entry, skip ':' delimiter */
325
326                 if ((*end) == ':')
327                         start = end + 1;
328                 else
329                         start = end;
330         }
331 }
332
333
334 /* suck_check_classbuffer_size *************************************************
335
336    Assert that at least <len> bytes are left to read <len> is limited
337    to the range of non-negative s4 values.
338
339 *******************************************************************************/
340
341 bool suck_check_classbuffer_size(classbuffer *cb, s4 len)
342 {
343 #ifdef ENABLE_VERIFIER
344         if (len < 0 || ((cb->data + cb->size) - cb->pos) < len) {
345                 *exceptionptr =
346                         new_classformaterror((cb)->class, "Truncated class file");
347
348                 return false;
349         }
350 #endif /* ENABLE_VERIFIER */
351
352         return true;
353 }
354
355
356 u1 suck_u1(classbuffer *cb)
357 {
358         u1 a;
359
360         a = SUCK_BE_U1(cb->pos);
361         cb->pos++;
362
363         return a;
364 }
365
366
367 u2 suck_u2(classbuffer *cb)
368 {
369         u2 a;
370
371         a = SUCK_BE_U2(cb->pos);
372         cb->pos += 2;
373
374         return a;
375 }
376
377
378 u4 suck_u4(classbuffer *cb)
379 {
380         u4 a;
381
382         a = SUCK_BE_U4(cb->pos);
383         cb->pos += 4;
384
385         return a;
386 }
387
388
389 u8 suck_u8(classbuffer *cb)
390 {
391 #if U8_AVAILABLE == 1
392         u8 a;
393
394         a = SUCK_BE_U8(cb->pos);
395         cb->pos += 8;
396
397         return a;
398 #else
399         u8 v;
400
401         v.high = suck_u4(cb);
402         v.low = suck_u4(cb);
403
404         return v;
405 #endif
406 }
407
408
409 float suck_float(classbuffer *cb)
410 {
411         float f;
412
413 #if WORDS_BIGENDIAN == 0
414         u1 buffer[4];
415         u2 i;
416
417         for (i = 0; i < 4; i++)
418                 buffer[3 - i] = suck_u1(cb);
419
420         MCOPY((u1 *) (&f), buffer, u1, 4);
421 #else
422         suck_nbytes((u1*) (&f), cb, 4);
423 #endif
424
425         if (sizeof(float) != 4) {
426                 *exceptionptr = new_internalerror("Incompatible float-format");
427
428                 /* XXX should we exit in such a case? */
429                 throw_exception_exit();
430         }
431         
432         return f;
433 }
434
435
436 double suck_double(classbuffer *cb)
437 {
438         double d;
439
440 #if WORDS_BIGENDIAN == 0
441         u1 buffer[8];
442         u2 i;   
443
444 #if defined(__ARM__) && defined(__ARMEL__) && !defined(__VFP_FP__)
445         /*
446          * On little endian ARM processors when using FPA, word order
447          * of doubles is still big endian. So take that into account
448          * here. When using VFP, word order of doubles follows byte
449          * order. (michi 2005/07/24)
450          */
451         for (i = 0; i < 4; i++)
452                 buffer[3 - i] = suck_u1(cb);
453         for (i = 0; i < 4; i++)
454                 buffer[7 - i] = suck_u1(cb);
455 #else
456         for (i = 0; i < 8; i++)
457                 buffer[7 - i] = suck_u1(cb);
458 #endif /* defined(__ARM__) && ... */
459
460         MCOPY((u1 *) (&d), buffer, u1, 8);
461 #else 
462         suck_nbytes((u1*) (&d), cb, 8);
463 #endif
464
465         if (sizeof(double) != 8) {
466                 *exceptionptr = new_internalerror("Incompatible double-format");
467
468                 /* XXX should we exit in such a case? */
469                 throw_exception_exit();
470         }
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 and of the function) */
527
528         filenamelen = utf_strlen(c->name) + strlen(".class") + strlen("0");
529         filename = MNEW(char, filenamelen);
530
531         utf_sprint(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 #if defined(USE_THREADS)
542                         /* enter a monitor on zip/jar archives */
543
544                         builtin_monitorenter((java_objectheader *) lce);
545 #endif
546
547                         /* try to get the file in current archive */
548
549                         cb = zip_get(lce, c);
550
551 #if defined(USE_THREADS)
552                         /* leave the monitor */
553
554                         builtin_monitorexit((java_objectheader *) lce);
555 #endif
556
557                 } else {
558 #endif /* defined(ENABLE_ZLIB) */
559                         path = MNEW(char, lce->pathlen + filenamelen);
560                         strcpy(path, lce->path);
561                         strcat(path, filename);
562
563                         classfile = fopen(path, "r");
564
565                         if (classfile) {                                   /* file exists */
566                                 if (!stat(path, &buffer)) {            /* read classfile data */
567                                         cb = NEW(classbuffer);
568                                         cb->class = c;
569                                         cb->size  = buffer.st_size;
570                                         cb->data  = MNEW(u1, cb->size);
571                                         cb->pos   = cb->data;
572                                         cb->path  = lce->path;
573
574                                         /* read class data */
575                                         len = fread(cb->data, 1, cb->size, classfile);
576
577                                         if (len != buffer.st_size) {
578                                                 suck_stop(cb);
579 /*                                              if (ferror(classfile)) { */
580 /*                                              } */
581                                         }
582                                 }
583                         }
584
585                         MFREE(path, char, lce->pathlen + filenamelen);
586 #if defined(ENABLE_ZLIB)
587                 }
588 #endif
589         }
590
591         if (opt_verbose)
592                 if (cb == NULL)
593                         dolog("Warning: Can not open class file '%s'", filename);
594
595         MFREE(filename, char, filenamelen);
596
597         return cb;
598 }
599
600
601 /* suck_stop *******************************************************************
602
603    Frees memory for buffer with classfile data.
604
605    CAUTION: This function may only be called if buffer has been
606    allocated by suck_start with reading a file.
607         
608 *******************************************************************************/
609
610 void suck_stop(classbuffer *cb)
611 {
612         /* free memory */
613
614         MFREE(cb->data, u1, cb->size);
615         FREE(cb, classbuffer);
616 }
617
618
619 /*
620  * These are local overrides for various environment variables in Emacs.
621  * Please do not remove this and leave it at the end of the file, where
622  * Emacs will automagically detect them.
623  * ---------------------------------------------------------------------
624  * Local variables:
625  * mode: c
626  * indent-tabs-mode: t
627  * c-basic-offset: 4
628  * tab-width: 4
629  * End:
630  */