* noinst_HEADERS: Removed md-abi.inc.
[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 R. Grafl, A. Krall, C. Kruegel, C. Oates,
4    R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
5    C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
6    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., 59 Temple Place - Suite 330, Boston, MA
23    02111-1307, USA.
24
25    Contact: cacao@complang.tuwien.ac.at
26
27    Authors: Christian Thalinger
28
29    Changes:
30
31    $Id: suck.c 4007 2005-12-22 16:26:03Z 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 */
211
212                         list_addlast(list_classpath_entries, lce);
213                 }
214
215                 /* goto next classpath entry, skip ':' delimiter */
216
217                 if ((*end) == ':') {
218                         start = end + 1;
219
220                 } else {
221                         start = end;
222                 }
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           *tmpbootclasspath;
246
247         /* get the property value */
248
249         value = properties_get(key);
250
251         if (value == NULL)
252                 return;
253
254         /* get the directory entries of the property */
255
256         for (start = value; (*start) != '\0'; ) {
257
258                 /* search for ':' delimiter to get the end of the current entry */
259
260                 for (end = start; ((*end) != '\0') && ((*end) != ':'); end++);
261
262                 /* found an entry */
263
264                 if (start != end) {
265                         /* allocate memory for the path entry */
266
267                         pathlen = end - start;
268                         path = MNEW(char, pathlen + strlen("0"));
269
270                         /* copy and terminate the string */
271
272                         strncpy(path, start, pathlen);
273                         path[pathlen] = '\0';
274
275                         /* scan the directory found for zip/jar files */
276
277                         n = scandir(path, &namelist, scandir_filter, alphasort);
278
279                         /* on error, just continue, this should be ok */
280
281                         if (n >= 0) {
282                                 for (i = 0; i < n; i++) {
283 #if defined(_DIRENT_HAVE_D_NAMLEN)
284                                         namlen = namelist[i]->d_namlen;
285 #else
286                                         namlen = strlen(namelist[i]->d_name);
287 #endif
288
289                                         /* reallocate memory for bootclasspath */
290
291                                         tmpbootclasspath = MNEW(char,
292                                                                                         pathlen + strlen("/") + namlen +
293                                                                                         strlen(":") +
294                                                                                         strlen(bootclasspath) +
295                                                                                         strlen("0"));
296
297                                         /* prepend the file found to bootclasspath */
298
299                                         strcpy(tmpbootclasspath, path);
300                                         strcat(tmpbootclasspath, "/");
301                                         strcat(tmpbootclasspath, namelist[i]->d_name);
302                                         strcat(tmpbootclasspath, ":");
303
304                                         strcat(tmpbootclasspath, bootclasspath);
305
306                                         /* free old bootclasspath memory */
307
308                                         MFREE(bootclasspath, u1, strlen(bootclasspath));
309
310                                         /* and set the new bootclasspath */
311
312                                         bootclasspath = tmpbootclasspath;
313
314                                         /* free the memory allocated by scandir */
315
316                                         FREE(namelist[i], struct dirent);
317                                 }
318
319                                 FREE(namelist, struct dirent);
320                         }
321
322                         MFREE(path, char, pathlen + strlen("0"));
323                 }
324
325                 /* goto next entry, skip ':' delimiter */
326
327                 if ((*end) == ':') {
328                         start = end + 1;
329
330                 } else {
331                         start = end;
332                 }
333         }
334 }
335
336
337 /* suck_check_classbuffer_size *************************************************
338
339    Assert that at least <len> bytes are left to read <len> is limited
340    to the range of non-negative s4 values.
341
342 *******************************************************************************/
343
344 bool suck_check_classbuffer_size(classbuffer *cb, s4 len)
345 {
346 #ifdef ENABLE_VERIFIER
347         if (len < 0 || ((cb->data + cb->size) - cb->pos) < len) {
348                 *exceptionptr =
349                         new_classformaterror((cb)->class, "Truncated class file");
350
351                 return false;
352         }
353 #endif /* ENABLE_VERIFIER */
354
355         return true;
356 }
357
358
359 u1 suck_u1(classbuffer *cb)
360 {
361         u1 a;
362
363         a = SUCK_BE_U1(cb->pos);
364         cb->pos++;
365
366         return a;
367 }
368
369
370 u2 suck_u2(classbuffer *cb)
371 {
372         u2 a;
373
374         a = SUCK_BE_U2(cb->pos);
375         cb->pos += 2;
376
377         return a;
378 }
379
380
381 u4 suck_u4(classbuffer *cb)
382 {
383         u4 a;
384
385         a = SUCK_BE_U4(cb->pos);
386         cb->pos += 4;
387
388         return a;
389 }
390
391
392 u8 suck_u8(classbuffer *cb)
393 {
394 #if U8_AVAILABLE == 1
395         u8 a;
396
397         a = SUCK_BE_U8(cb->pos);
398         cb->pos += 8;
399
400         return a;
401 #else
402         u8 v;
403
404         v.high = suck_u4(cb);
405         v.low = suck_u4(cb);
406
407         return v;
408 #endif
409 }
410
411
412 float suck_float(classbuffer *cb)
413 {
414         float f;
415
416 #if WORDS_BIGENDIAN == 0
417         u1 buffer[4];
418         u2 i;
419
420         for (i = 0; i < 4; i++)
421                 buffer[3 - i] = suck_u1(cb);
422
423         MCOPY((u1 *) (&f), buffer, u1, 4);
424 #else
425         suck_nbytes((u1*) (&f), cb, 4);
426 #endif
427
428         if (sizeof(float) != 4) {
429                 *exceptionptr = new_internalerror("Incompatible float-format");
430
431                 /* XXX should we exit in such a case? */
432                 throw_exception_exit();
433         }
434         
435         return f;
436 }
437
438
439 double suck_double(classbuffer *cb)
440 {
441         double d;
442
443 #if WORDS_BIGENDIAN == 0
444         u1 buffer[8];
445         u2 i;   
446
447 #if defined(__ARM__) && defined(__ARMEL__) && !defined(__VFP_FP__)
448         /*
449          * On little endian ARM processors when using FPA, word order
450          * of doubles is still big endian. So take that into account
451          * here. When using VFP, word order of doubles follows byte
452          * order. (michi 2005/07/24)
453          */
454         for (i = 0; i < 4; i++)
455                 buffer[3 - i] = suck_u1(cb);
456         for (i = 0; i < 4; i++)
457                 buffer[7 - i] = suck_u1(cb);
458 #else
459         for (i = 0; i < 8; i++)
460                 buffer[7 - i] = suck_u1(cb);
461 #endif /* defined(__ARM__) && ... */
462
463         MCOPY((u1 *) (&d), buffer, u1, 8);
464 #else 
465         suck_nbytes((u1*) (&d), cb, 8);
466 #endif
467
468         if (sizeof(double) != 8) {
469                 *exceptionptr = new_internalerror("Incompatible double-format");
470
471                 /* XXX should we exit in such a case? */
472                 throw_exception_exit();
473         }
474         
475         return d;
476 }
477
478
479 /* suck_nbytes *****************************************************************
480
481    Transfer block of classfile data into a buffer.
482
483 *******************************************************************************/
484
485 void suck_nbytes(u1 *buffer, classbuffer *cb, s4 len)
486 {
487         MCOPY(buffer, cb->pos, u1, len);
488         cb->pos += len;
489 }
490
491
492 /* suck_skip_nbytes ************************************************************
493
494    Skip block of classfile data.
495
496 *******************************************************************************/
497
498 void suck_skip_nbytes(classbuffer *cb, s4 len)
499 {
500         cb->pos += len;
501 }
502
503
504 /* suck_start ******************************************************************
505
506    Returns true if classbuffer is already loaded or a file for the
507    specified class has succussfully been read in. All directories of
508    the searchpath are used to find the classfile (<classname>.class).
509    Returns NULL if no classfile is found and writes an error message.
510         
511 *******************************************************************************/
512
513 classbuffer *suck_start(classinfo *c)
514 {
515         list_classpath_entry *lce;
516         char                 *filename;
517         s4                    filenamelen;
518         char                 *path;
519         FILE                 *classfile;
520         s4                    len;
521         struct stat           buffer;
522         classbuffer          *cb;
523
524         /* initialize return value */
525
526         cb = NULL;
527
528         /* get the classname as char string (do it here for the warning at
529        the and of the function) */
530
531         filenamelen = utf_strlen(c->name) + strlen(".class") + strlen("0");
532         filename = MNEW(char, filenamelen);
533
534         utf_sprint(filename, c->name);
535         strcat(filename, ".class");
536
537         /* walk through all classpath entries */
538
539         for (lce = list_first(list_classpath_entries); lce != NULL && cb == NULL;
540                  lce = list_next(list_classpath_entries, lce)) {
541 #if defined(ENABLE_ZLIB)
542                 if (lce->type == CLASSPATH_ARCHIVE) {
543
544 #if defined(USE_THREADS)
545                         /* enter a monitor on zip/jar archives */
546
547                         builtin_monitorenter((java_objectheader *) lce);
548 #endif
549
550                         /* try to get the file in current archive */
551
552                         cb = zip_get(lce, c);
553
554 #if defined(USE_THREADS)
555                         /* leave the monitor */
556
557                         builtin_monitorexit((java_objectheader *) lce);
558 #endif
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                                         len = fread(cb->data, 1, cb->size, classfile);
579
580                                         if (len != buffer.st_size) {
581                                                 suck_stop(cb);
582 /*                                              if (ferror(classfile)) { */
583 /*                                              } */
584                                         }
585                                 }
586                         }
587
588                         MFREE(path, char, lce->pathlen + filenamelen);
589 #if defined(ENABLE_ZLIB)
590                 }
591 #endif
592         }
593
594         if (opt_verbose)
595                 if (cb == NULL)
596                         dolog("Warning: Can not open class file '%s'", filename);
597
598         MFREE(filename, char, filenamelen);
599
600         return cb;
601 }
602
603
604 /* suck_stop *******************************************************************
605
606    Frees memory for buffer with classfile data.
607
608    CAUTION: This function may only be called if buffer has been
609    allocated by suck_start with reading a file.
610         
611 *******************************************************************************/
612
613 void suck_stop(classbuffer *cb)
614 {
615         /* free memory */
616
617         MFREE(cb->data, u1, cb->size);
618         FREE(cb, classbuffer);
619 }
620
621
622 /*
623  * These are local overrides for various environment variables in Emacs.
624  * Please do not remove this and leave it at the end of the file, where
625  * Emacs will automagically detect them.
626  * ---------------------------------------------------------------------
627  * Local variables:
628  * mode: c
629  * indent-tabs-mode: t
630  * c-basic-offset: 4
631  * tab-width: 4
632  * End:
633  */