* src/vm/suck.c (suck_add): Fixed compiler warning with #ifndef
[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 4415 2006-02-03 21:58:44Z 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 #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) {
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                                 throw_cacao_exception_exit(string_java_lang_InternalError,
195                                                                                    "zip/jar files not supported");
196 #endif
197                                 
198                         } else {
199                                 if (filename[filenamelen - 1] != '/') {/* XXX fixme for win32 */
200                                         filename[filenamelen] = '/';
201                                         filename[filenamelen + 1] = '\0';
202                                         filenamelen++;
203                                 }
204
205                                 lce = NEW(list_classpath_entry);
206
207                                 lce->type    = CLASSPATH_PATH;
208                                 lce->path    = filename;
209                                 lce->pathlen = filenamelen;
210                         }
211
212                         /* add current classpath entry, if no error */
213
214                         if (lce)
215                                 list_addlast(list_classpath_entries, lce);
216                 }
217
218                 /* goto next classpath entry, skip ':' delimiter */
219
220                 if ((*end) == ':')
221                         start = end + 1;
222                 else
223                         start = end;
224         }
225 }
226
227
228 /* suck_add_from_property ******************************************************
229
230    Adds a classpath form a property entry to the global classpath
231    entries list.
232
233 *******************************************************************************/
234
235 void suck_add_from_property(char *key)
236 {
237         char           *value;
238         char           *start;
239         char           *end;
240         char           *path;
241         s4              pathlen;
242         struct dirent **namelist;
243         s4              n;
244         s4              i;
245         s4              namlen;
246         char           *tmpbootclasspath;
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                         /* scan the directory found for zip/jar files */
277
278                         n = 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                                         /* reallocate memory for bootclasspath */
291
292                                         tmpbootclasspath = MNEW(char,
293                                                                                         pathlen + strlen("/") + namlen +
294                                                                                         strlen(":") +
295                                                                                         strlen(bootclasspath) +
296                                                                                         strlen("0"));
297
298                                         /* prepend the file found to bootclasspath */
299
300                                         strcpy(tmpbootclasspath, path);
301                                         strcat(tmpbootclasspath, "/");
302                                         strcat(tmpbootclasspath, namelist[i]->d_name);
303                                         strcat(tmpbootclasspath, ":");
304
305                                         strcat(tmpbootclasspath, bootclasspath);
306
307                                         /* free old bootclasspath memory */
308
309                                         MFREE(bootclasspath, u1, strlen(bootclasspath));
310
311                                         /* and set the new bootclasspath */
312
313                                         bootclasspath = tmpbootclasspath;
314
315                                         /* free the memory allocated by scandir */
316
317                                         FREE(namelist[i], struct dirent);
318                                 }
319
320                                 FREE(namelist, struct dirent);
321                         }
322
323                         MFREE(path, char, pathlen + strlen("0"));
324                 }
325
326                 /* goto next entry, skip ':' delimiter */
327
328                 if ((*end) == ':')
329                         start = end + 1;
330                 else
331                         start = end;
332         }
333 }
334
335
336 /* suck_check_classbuffer_size *************************************************
337
338    Assert that at least <len> bytes are left to read <len> is limited
339    to the range of non-negative s4 values.
340
341 *******************************************************************************/
342
343 bool suck_check_classbuffer_size(classbuffer *cb, s4 len)
344 {
345 #ifdef ENABLE_VERIFIER
346         if (len < 0 || ((cb->data + cb->size) - cb->pos) < len) {
347                 *exceptionptr =
348                         new_classformaterror((cb)->class, "Truncated class file");
349
350                 return false;
351         }
352 #endif /* ENABLE_VERIFIER */
353
354         return true;
355 }
356
357
358 u1 suck_u1(classbuffer *cb)
359 {
360         u1 a;
361
362         a = SUCK_BE_U1(cb->pos);
363         cb->pos++;
364
365         return a;
366 }
367
368
369 u2 suck_u2(classbuffer *cb)
370 {
371         u2 a;
372
373         a = SUCK_BE_U2(cb->pos);
374         cb->pos += 2;
375
376         return a;
377 }
378
379
380 u4 suck_u4(classbuffer *cb)
381 {
382         u4 a;
383
384         a = SUCK_BE_U4(cb->pos);
385         cb->pos += 4;
386
387         return a;
388 }
389
390
391 u8 suck_u8(classbuffer *cb)
392 {
393 #if U8_AVAILABLE == 1
394         u8 a;
395
396         a = SUCK_BE_U8(cb->pos);
397         cb->pos += 8;
398
399         return a;
400 #else
401         u8 v;
402
403         v.high = suck_u4(cb);
404         v.low = suck_u4(cb);
405
406         return v;
407 #endif
408 }
409
410
411 float suck_float(classbuffer *cb)
412 {
413         float f;
414
415 #if WORDS_BIGENDIAN == 0
416         u1 buffer[4];
417         u2 i;
418
419         for (i = 0; i < 4; i++)
420                 buffer[3 - i] = suck_u1(cb);
421
422         MCOPY((u1 *) (&f), buffer, u1, 4);
423 #else
424         suck_nbytes((u1*) (&f), cb, 4);
425 #endif
426
427         if (sizeof(float) != 4) {
428                 *exceptionptr = new_internalerror("Incompatible float-format");
429
430                 /* XXX should we exit in such a case? */
431                 throw_exception_exit();
432         }
433         
434         return f;
435 }
436
437
438 double suck_double(classbuffer *cb)
439 {
440         double d;
441
442 #if WORDS_BIGENDIAN == 0
443         u1 buffer[8];
444         u2 i;   
445
446 #if defined(__ARM__) && defined(__ARMEL__) && !defined(__VFP_FP__)
447         /*
448          * On little endian ARM processors when using FPA, word order
449          * of doubles is still big endian. So take that into account
450          * here. When using VFP, word order of doubles follows byte
451          * order. (michi 2005/07/24)
452          */
453         for (i = 0; i < 4; i++)
454                 buffer[3 - i] = suck_u1(cb);
455         for (i = 0; i < 4; i++)
456                 buffer[7 - i] = suck_u1(cb);
457 #else
458         for (i = 0; i < 8; i++)
459                 buffer[7 - i] = suck_u1(cb);
460 #endif /* defined(__ARM__) && ... */
461
462         MCOPY((u1 *) (&d), buffer, u1, 8);
463 #else 
464         suck_nbytes((u1*) (&d), cb, 8);
465 #endif
466
467         if (sizeof(double) != 8) {
468                 *exceptionptr = new_internalerror("Incompatible double-format");
469
470                 /* XXX should we exit in such a case? */
471                 throw_exception_exit();
472         }
473         
474         return d;
475 }
476
477
478 /* suck_nbytes *****************************************************************
479
480    Transfer block of classfile data into a buffer.
481
482 *******************************************************************************/
483
484 void suck_nbytes(u1 *buffer, classbuffer *cb, s4 len)
485 {
486         MCOPY(buffer, cb->pos, u1, len);
487         cb->pos += len;
488 }
489
490
491 /* suck_skip_nbytes ************************************************************
492
493    Skip block of classfile data.
494
495 *******************************************************************************/
496
497 void suck_skip_nbytes(classbuffer *cb, s4 len)
498 {
499         cb->pos += len;
500 }
501
502
503 /* suck_start ******************************************************************
504
505    Returns true if classbuffer is already loaded or a file for the
506    specified class has succussfully been read in. All directories of
507    the searchpath are used to find the classfile (<classname>.class).
508    Returns NULL if no classfile is found and writes an error message.
509         
510 *******************************************************************************/
511
512 classbuffer *suck_start(classinfo *c)
513 {
514         list_classpath_entry *lce;
515         char                 *filename;
516         s4                    filenamelen;
517         char                 *path;
518         FILE                 *classfile;
519         s4                    len;
520         struct stat           buffer;
521         classbuffer          *cb;
522
523         /* initialize return value */
524
525         cb = NULL;
526
527         /* get the classname as char string (do it here for the warning at
528        the and of the function) */
529
530         filenamelen = utf_strlen(c->name) + strlen(".class") + strlen("0");
531         filename = MNEW(char, filenamelen);
532
533         utf_sprint(filename, c->name);
534         strcat(filename, ".class");
535
536         /* walk through all classpath entries */
537
538         for (lce = list_first(list_classpath_entries); lce != NULL && cb == NULL;
539                  lce = list_next(list_classpath_entries, lce)) {
540 #if defined(ENABLE_ZLIB)
541                 if (lce->type == CLASSPATH_ARCHIVE) {
542
543 #if defined(USE_THREADS)
544                         /* enter a monitor on zip/jar archives */
545
546                         builtin_monitorenter((java_objectheader *) lce);
547 #endif
548
549                         /* try to get the file in current archive */
550
551                         cb = zip_get(lce, c);
552
553 #if defined(USE_THREADS)
554                         /* leave the monitor */
555
556                         builtin_monitorexit((java_objectheader *) lce);
557 #endif
558
559                 } else {
560 #endif /* defined(ENABLE_ZLIB) */
561                         path = MNEW(char, lce->pathlen + filenamelen);
562                         strcpy(path, lce->path);
563                         strcat(path, filename);
564
565                         classfile = fopen(path, "r");
566
567                         if (classfile) {                                   /* file exists */
568                                 if (!stat(path, &buffer)) {            /* read classfile data */
569                                         cb = NEW(classbuffer);
570                                         cb->class = c;
571                                         cb->size  = buffer.st_size;
572                                         cb->data  = MNEW(u1, cb->size);
573                                         cb->pos   = cb->data;
574                                         cb->path  = lce->path;
575
576                                         /* read class data */
577                                         len = fread(cb->data, 1, cb->size, classfile);
578
579                                         if (len != buffer.st_size) {
580                                                 suck_stop(cb);
581 /*                                              if (ferror(classfile)) { */
582 /*                                              } */
583                                         }
584                                 }
585                         }
586
587                         MFREE(path, char, lce->pathlen + filenamelen);
588 #if defined(ENABLE_ZLIB)
589                 }
590 #endif
591         }
592
593         if (opt_verbose)
594                 if (cb == NULL)
595                         dolog("Warning: Can not open class file '%s'", filename);
596
597         MFREE(filename, char, filenamelen);
598
599         return cb;
600 }
601
602
603 /* suck_stop *******************************************************************
604
605    Frees memory for buffer with classfile data.
606
607    CAUTION: This function may only be called if buffer has been
608    allocated by suck_start with reading a file.
609         
610 *******************************************************************************/
611
612 void suck_stop(classbuffer *cb)
613 {
614         /* free memory */
615
616         MFREE(cb->data, u1, cb->size);
617         FREE(cb, classbuffer);
618 }
619
620
621 /*
622  * These are local overrides for various environment variables in Emacs.
623  * Please do not remove this and leave it at the end of the file, where
624  * Emacs will automagically detect them.
625  * ---------------------------------------------------------------------
626  * Local variables:
627  * mode: c
628  * indent-tabs-mode: t
629  * c-basic-offset: 4
630  * tab-width: 4
631  * End:
632  */