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