OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / native / jni / java-io / java_io_VMFile.c
1 /* java_io_VMFile.c - Native methods for java.io.File class
2    Copyright (C) 1998, 2004, 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 /* do not move; needed here because of some macro definitions */
39 #include <config.h>
40
41 #include <assert.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44
45 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <unistd.h>
49 #endif
50
51 #include <jni.h>
52 #include <jcl.h>
53 #include "cpio.h"
54 #include "cpnative.h"
55
56 #include "java_io_VMFile.h"
57
58 /*************************************************************************/
59
60 /*
61  * Method to create an empty file.
62  *
63  * Class:     java_io_VMFile
64  * Method:    create
65  * Signature: (Ljava/lang/String;)Z
66  */
67
68 JNIEXPORT jboolean JNICALL
69 Java_java_io_VMFile_create (JNIEnv * env,
70                             jclass clazz __attribute__ ((__unused__)),
71                             jstring name)
72 {
73 #ifndef WITHOUT_FILESYSTEM
74   const char *filename;
75   int fd;
76   int result;
77
78   filename = JCL_jstring_to_cstring (env, name);
79   if (filename == NULL)
80     {
81       return 0;
82     }
83
84   result = cpio_openFile (filename, &fd, CPFILE_FLAG_CREATE|CPFILE_FLAG_WRITE, CPFILE_PERMISSION_NORMAL);
85   if (result != CPNATIVE_OK)
86     {
87       if (result != EEXIST)
88         JCL_ThrowException (env,
89                             "java/io/IOException",
90                             cpnative_getErrorString (result));
91       JCL_free_cstring (env, name, filename);
92       return 0;
93     }
94   cpio_closeFile (fd);
95
96   JCL_free_cstring (env, name, filename);
97   return 1;
98 #else /* not WITHOUT_FILESYSTEM */
99   return 0;
100 #endif /* not WITHOUT_FILESYSTEM */
101 }
102
103 /*************************************************************************/
104
105 /*
106  * This method checks to see if we have read permission on a file.
107  *
108  * Class:     java_io_VMFile
109  * Method:    canRead
110  * Signature: (Ljava/lang/String;)Z
111  */
112
113 JNIEXPORT jboolean JNICALL
114 Java_java_io_VMFile_canRead (JNIEnv * env,
115                              jobject obj __attribute__ ((__unused__)),
116                              jstring name)
117 {
118 #ifndef WITHOUT_FILESYSTEM
119   const char *filename;
120   int fd;
121   int result;
122
123   /* Don't use the JCL convert function because it throws an exception
124      on failure */
125   filename = (*env)->GetStringUTFChars (env, name, 0);
126   if (filename == NULL)
127     {
128       return 0;
129     }
130
131   /* The lazy man's way out.  We actually do open the file for reading
132      briefly to verify it can be done */
133   result = cpio_openFile (filename, &fd, CPFILE_FLAG_READ, 0);
134   (*env)->ReleaseStringUTFChars (env, name, filename);
135   if (result != CPNATIVE_OK)
136     return 0;
137   cpio_closeFile (fd);
138
139   return 1;
140 #else /* not WITHOUT_FILESYSTEM */
141   return 0;
142 #endif /* not WITHOUT_FILESYSTEM */
143 }
144
145 /*************************************************************************/
146
147 /*
148  * This method checks to see if we have write permission on a file.
149  *
150  * Class:     java_io_VMFile
151  * Method:    canWrite
152  * Signature: (Ljava/lang/String;)Z
153  */
154
155 JNIEXPORT jboolean JNICALL
156 Java_java_io_VMFile_canWrite (JNIEnv * env,
157                               jobject obj __attribute__ ((__unused__)),
158                               jstring name)
159 {
160 #ifndef WITHOUT_FILESYSTEM
161   const char *filename;
162   int fd;
163   int result;
164
165   /* Don't use the JCL convert function because it throws an exception
166      on failure */
167   filename = (*env)->GetStringUTFChars (env, name, 0);
168   if (filename == NULL)
169     {
170       return 0;
171     }
172
173   /* The lazy man's way out.  We actually do open the file for writing
174      briefly to verify it can be done */
175   result = cpio_openFile (filename, &fd, CPFILE_FLAG_READWRITE, 0);
176   (*env)->ReleaseStringUTFChars (env, name, filename);
177   if (result != CPNATIVE_OK)
178     {
179       return 0;
180     }
181   cpio_closeFile (fd);
182
183   return 1;
184 #else /* not WITHOUT_FILESYSTEM */
185   return 0;
186 #endif /* not WITHOUT_FILESYSTEM */
187 }
188
189 /*************************************************************************/
190
191 /*
192  * This method makes a file read only.
193  *
194  * Class:     java_io_VMFile
195  * Method:    setReadOnly
196  * Signature: (Ljava/lang/String;)Z
197  */
198
199 JNIEXPORT jboolean JNICALL
200 Java_java_io_VMFile_setReadOnly (JNIEnv * env,
201                                  jobject obj __attribute__ ((__unused__)),
202                                  jstring name)
203 {
204 #ifndef WITHOUT_FILESYSTEM
205   const char *filename;
206   int result;
207
208   /* Don't use the JCL convert function because it throws an exception
209      on failure */
210   filename = (*env)->GetStringUTFChars (env, name, 0);
211   if (filename == NULL)
212     {
213       return 0;
214     }
215
216   result = cpio_setFileReadonly (filename);
217   (*env)->ReleaseStringUTFChars (env, name, filename);
218
219   return result == CPNATIVE_OK ? 1 : 0;
220 #else /* not WITHOUT_FILESYSTEM */
221   return 0;
222 #endif /* not WITHOUT_FILESYSTEM */
223 }
224
225 /*************************************************************************/
226
227 /*
228  * This method checks to see if a file exists.
229  *
230  * Class:     java_io_VMFile
231  * Method:    exists
232  * Signature: (Ljava/lang/String;)Z
233  */
234
235 JNIEXPORT jboolean JNICALL
236 Java_java_io_VMFile_exists (JNIEnv * env,
237                             jobject obj __attribute__ ((__unused__)),
238                             jstring name)
239 {
240 #ifndef WITHOUT_FILESYSTEM
241   const char *filename;
242   int result;
243
244   /* Don't use the JCL convert function because it throws an exception
245      on failure */
246   filename = (*env)->GetStringUTFChars (env, name, 0);
247   if (filename == NULL)
248     {
249       return 0;
250     }
251
252   result = cpio_isFileExists (filename);
253   (*env)->ReleaseStringUTFChars (env, name, filename);
254
255   return result == CPNATIVE_OK ? 1 : 0;
256 #else /* not WITHOUT_FILESYSTEM */
257   return 0;
258 #endif /* not WITHOUT_FILESYSTEM */
259 }
260
261 /*************************************************************************/
262
263 /*
264  * This method checks to see if a file is a "plain" file; that is, not
265  * a directory, pipe, etc.
266  *
267  * Class:     java_io_VMFile
268  * Method:    isFile
269  * Signature: (Ljava/lang/String;)Z
270  */
271
272 JNIEXPORT jboolean JNICALL
273 Java_java_io_VMFile_isFile (JNIEnv * env,
274                             jobject obj __attribute__ ((__unused__)),
275                             jstring name)
276 {
277 #ifndef WITHOUT_FILESYSTEM
278   const char *filename;
279   int result;
280   jint entryType;
281
282   /* Don't use the JCL convert function because it throws an exception
283      on failure */
284   filename = (*env)->GetStringUTFChars (env, name, 0);
285   if (filename == NULL)
286     {
287       return 0;
288     }
289
290   result = cpio_checkType (filename, &entryType);
291   (*env)->ReleaseStringUTFChars (env, name, filename);
292
293   return result == CPNATIVE_OK && entryType == CPFILE_FILE ? 1 : 0;
294 #else /* not WITHOUT_FILESYSTEM */
295   return 0;
296 #endif /* not WITHOUT_FILESYSTEM */
297 }
298
299 /*************************************************************************/
300
301 /*
302  * This method checks to see if a file is a directory or not.
303  *
304  * Class:     java_io_VMFile
305  * Method:    isDirectory
306  * Signature: (Ljava/lang/String;)Z
307  */
308
309 JNIEXPORT jboolean JNICALL
310 Java_java_io_VMFile_isDirectory (JNIEnv * env,
311                                  jobject obj __attribute__ ((__unused__)),
312                                  jstring name)
313 {
314 #ifndef WITHOUT_FILESYSTEM
315   const char *filename;
316   int result;
317   jint entryType;
318
319   /* Don't use the JCL convert function because it throws an exception
320      on failure */
321   filename = (*env)->GetStringUTFChars (env, name, 0);
322   if (filename == NULL)
323     {
324       return 0;
325     }
326   
327   result = cpio_checkType (filename, &entryType);
328   (*env)->ReleaseStringUTFChars (env, name, filename);
329
330   return result == CPNATIVE_OK && entryType == CPFILE_DIRECTORY ? 1 : 0;
331 #else /* not WITHOUT_FILESYSTEM */
332   return 0;
333 #endif /* not WITHOUT_FILESYSTEM */
334 }
335
336 /*************************************************************************/
337
338 /*
339  * This method returns the length of the file.
340  *
341  * Class:     java_io_VMFile
342  * Method:    length
343  * Signature: (Ljava/lang/String;)J
344  */
345
346 JNIEXPORT jlong JNICALL
347 Java_java_io_VMFile_length (JNIEnv * env,
348                             jobject obj __attribute__ ((__unused__)),
349                             jstring name)
350 {
351 #ifndef WITHOUT_FILESYSTEM
352   const char *filename;
353   int tmpfd;
354   jlong length;
355   int result;
356
357   /* Don't use the JCL convert function because it throws an exception
358      on failure */
359   filename = (*env)->GetStringUTFChars (env, name, 0);
360   if (filename == NULL)
361     return 0;
362
363   /* open file for reading, get size and close file */
364   result = cpio_openFile (filename, &tmpfd, CPFILE_FLAG_READ, 0);
365   if (result != CPNATIVE_OK)
366     return 0;
367
368   result = cpio_getFileSize (tmpfd, &length);
369   if (result != CPNATIVE_OK)
370     {
371       cpio_closeFile (tmpfd);
372       return 0;
373     }
374
375   result = cpio_closeFile (tmpfd);
376   (*env)->ReleaseStringUTFChars (env, name, filename);
377
378   return result == CPNATIVE_OK ? length : 0;
379 #else /* not WITHOUT_FILESYSTEM */
380   return 0;
381 #endif /* not WITHOUT_FILESYSTEM */
382 }
383
384 /*************************************************************************/
385
386 /*
387  * This method returns the modification date of the file.
388  *
389  * Class:     java_io_VMFile
390  * Method:    lastModified
391  * Signature: (Ljava/lang/String;)J
392  */
393
394 JNIEXPORT jlong JNICALL
395 Java_java_io_VMFile_lastModified (JNIEnv * env,
396                                   jobject obj __attribute__ ((__unused__)),
397                                   jstring name)
398 {
399 #ifndef WITHOUT_FILESYSTEM
400   const char *filename;
401   jlong mtime;
402   int result;
403
404   /* Don't use the JCL convert function because it throws an exception
405      on failure */
406   filename = (*env)->GetStringUTFChars (env, name, 0);
407   if (filename == NULL)
408     {
409       return 0;
410     }
411
412   result = cpio_getModificationTime (filename, &mtime);
413   (*env)->ReleaseStringUTFChars (env, name, filename);
414
415   return result == CPNATIVE_OK ? mtime : 0;
416 #else /* not WITHOUT_FILESYSTEM */
417   return 0;
418 #endif /* not WITHOUT_FILESYSTEM */
419 }
420
421 /*************************************************************************/
422
423 /*
424  * This method sets the modification date of the file.
425  *
426  * Class:     java_io_VMFile
427  * Method:    setLastModified
428  * Signature: (Ljava/lang/String;J)Z
429  */
430
431 JNIEXPORT jboolean JNICALL
432 Java_java_io_VMFile_setLastModified (JNIEnv * env,
433                                      jobject obj __attribute__ ((__unused__)),
434                                      jstring name, jlong newtime)
435 {
436 #ifndef WITHOUT_FILESYSTEM
437   const char *filename;
438   int result;
439
440   /* Don't use the JCL convert function because it throws an exception
441      on failure */
442   filename = (*env)->GetStringUTFChars (env, name, 0);
443   if (filename == NULL)
444     {
445       return 0;
446     }
447
448   result = cpio_setModificationTime (filename, newtime);
449   (*env)->ReleaseStringUTFChars (env, name, filename);
450
451   return result == CPNATIVE_OK ? 1 : 0;
452 #else /* not WITHOUT_FILESYSTEM */
453   return 0;
454 #endif /* not WITHOUT_FILESYSTEM */
455 }
456
457 /*************************************************************************/
458
459 /*
460  * This method deletes a file (actually a name for a file - additional
461  * links could exist).
462  *
463  * Class:     java_io_VMFile
464  * Method:    delete
465  * Signature: (Ljava/lang/String;)Z
466  */
467
468 JNIEXPORT jboolean JNICALL
469 Java_java_io_VMFile_delete (JNIEnv * env,
470                             jobject obj __attribute__ ((__unused__)),
471                             jstring name)
472 {
473 #ifndef WITHOUT_FILESYSTEM
474   const char *filename;
475   int result;
476
477   /* Don't use the JCL convert function because it throws an exception
478      on failure */
479   filename = (*env)->GetStringUTFChars (env, name, 0);
480   if (filename == NULL)
481     {
482       return 0;
483     }
484
485   result = cpio_removeFile (filename);
486   (*env)->ReleaseStringUTFChars (env, name, filename);
487
488   return result == CPNATIVE_OK ? 1 : 0;
489 #else /* not WITHOUT_FILESYSTEM */
490   return 0;
491 #endif /* not WITHOUT_FILESYSTEM */
492 }
493
494 /*************************************************************************/
495
496 /*
497  * This method creates a directory.
498  *
499  * Class:     java_io_VMFile
500  * Method:    mkdir
501  * Signature: (Ljava/lang/String;)Z
502  */
503
504 JNIEXPORT jboolean JNICALL
505 Java_java_io_VMFile_mkdir (JNIEnv * env,
506                            jobject obj __attribute__ ((__unused__)),
507                            jstring name)
508 {
509 #ifndef WITHOUT_FILESYSTEM
510   const char *pathname;
511   int result;
512
513   /* Don't use the JCL convert function because it throws an exception
514      on failure */
515   pathname = (*env)->GetStringUTFChars (env, name, 0);
516   if (pathname == NULL)
517     {
518       return 0;
519     }
520
521   result = cpio_mkdir (pathname);
522   (*env)->ReleaseStringUTFChars (env, name, pathname);
523
524   return (result == CPNATIVE_OK) ? 1 : 0;
525 #else /* not WITHOUT_FILESYSTEM */
526   return 0;
527 #endif /* not WITHOUT_FILESYSTEM */
528 }
529
530 /*************************************************************************/
531
532 /*
533  * This method renames a (link to a) file.
534  *
535  * Class:     java_io_VMFile
536  * Method:    renameTo
537  * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
538  */
539
540 JNIEXPORT jboolean JNICALL
541 Java_java_io_VMFile_renameTo (JNIEnv * env,
542                               jobject obj __attribute__ ((__unused__)),
543                               jstring t, jstring d)
544 {
545 #ifndef WITHOUT_FILESYSTEM
546   const char *old_filename, *new_filename;
547   int result;
548
549   /* Don't use the JCL convert function because it throws an exception
550      on failure */
551   old_filename = (*env)->GetStringUTFChars (env, t, 0);
552   if (old_filename == NULL)
553     {
554       return 0;
555     }
556
557   new_filename = (*env)->GetStringUTFChars (env, d, 0);
558   if (new_filename == NULL)
559     {
560       (*env)->ReleaseStringUTFChars (env, t, old_filename);
561       return 0;
562     }
563
564   result = cpio_rename (old_filename, new_filename);
565   (*env)->ReleaseStringUTFChars (env, d, new_filename);
566   (*env)->ReleaseStringUTFChars (env, t, old_filename);
567
568   return (result == CPNATIVE_OK) ? 1 : 0;
569 #else /* not WITHOUT_FILESYSTEM */
570   return 0;
571 #endif /* not WITHOUT_FILESYSTEM */
572 }
573
574 /*************************************************************************/
575
576 /*
577  * This method returns an array of String representing all the files
578  * in a directory except "." and "..".
579  *
580  * Class:     java_io_VMFile
581  * Method:    list
582  * Signature: (Ljava/lang/String;)[Ljava/lang/String;
583  */
584
585 JNIEXPORT jobjectArray JNICALL
586 Java_java_io_VMFile_list (JNIEnv * env, jobject obj
587                           __attribute__ ((__unused__)), jstring name)
588 {
589 #ifndef WITHOUT_FILESYSTEM
590   const int REALLOC_SIZE = 10;
591
592   const char *dirname;
593   int result;
594   char **filelist;
595   void *handle;
596   char *filename = (char *) JCL_malloc (env, FILENAME_MAX);
597   unsigned long int filelist_count, max_filelist_count;
598   char **tmp_filelist;
599   jclass str_clazz;
600   jobjectArray filearray;
601   unsigned long int i;
602   jstring str;
603
604   /* Don't use the JCL convert function because it throws an exception
605      on failure */
606   dirname = (*env)->GetStringUTFChars (env, name, 0);
607   if (dirname == NULL)
608     {
609       return 0;
610     }
611
612   /* open directory for reading */
613   result = cpio_openDir (dirname, &handle);
614
615   (*env)->ReleaseStringUTFChars (env, name, dirname);
616
617   if (result != CPNATIVE_OK)
618     {
619       return 0;
620     }
621
622   /* allocate filelist */
623   filelist = (char **) JCL_malloc (env, sizeof (char *) * REALLOC_SIZE);
624   if (filelist == NULL)
625     {
626       result = cpio_closeDir (handle);
627       return 0;
628     }
629   filelist_count = 0;
630   max_filelist_count = REALLOC_SIZE;
631
632   /* read the files from the directory */
633   result = cpio_readDir (handle, filename);
634   while (result == CPNATIVE_OK)
635     {
636       if ((strcmp (filename, ".") != 0) && (strcmp (filename, "..") != 0))
637         {
638           /* allocate more memory if necessary */
639           if (filelist_count >= max_filelist_count)
640             {
641               tmp_filelist = (char **) JCL_realloc (env,
642                                                     filelist,
643                                                     (max_filelist_count +
644                                                      REALLOC_SIZE) *
645                                                     sizeof (char *));
646               if (tmp_filelist == NULL)
647                 {
648                   for (i = 0; i < filelist_count; i++)
649                     {
650                       JCL_free (env, filelist[i]);
651                     }
652                   JCL_free (env, filelist);
653                   result = cpio_closeDir (handle);
654                   return 0;
655                 }
656               filelist = tmp_filelist;
657               max_filelist_count += REALLOC_SIZE;
658             }
659
660           /* save entry in list (avoid strdup, because it is not ANSI C, thus difficult to port) */
661           filelist[filelist_count] =
662             (char *) JCL_malloc (env, strlen (filename) + 1);
663           assert (filelist[filelist_count] != NULL);
664           strcpy (filelist[filelist_count], filename);
665           filelist_count++;
666         }
667
668       /* read next directory entry */
669       result = cpio_readDir (handle, filename);
670     }
671
672   JCL_free (env, filename);
673
674   /* close directory */
675   result = cpio_closeDir (handle);
676
677   /* put the list of files into a Java String array and return it */
678   str_clazz = (*env)->FindClass (env, "java/lang/String");
679   if (str_clazz == NULL)
680     {
681       for (i = 0; i < filelist_count; i++)
682         {
683           JCL_free (env, filelist[i]);
684         }
685       JCL_free (env, filelist);
686       return 0;
687     }
688   filearray = (*env)->NewObjectArray (env, filelist_count, str_clazz, 0);
689   if (filearray == NULL)
690     {
691       for (i = 0; i < filelist_count; i++)
692         {
693           JCL_free (env, filelist[i]);
694         }
695       JCL_free (env, filelist);
696       return 0;
697     }
698
699   (*env)->DeleteLocalRef (env, str_clazz);
700
701   for (i = 0; i < filelist_count; i++)
702     {
703       /* create new string */
704       str = (*env)->NewStringUTF (env, filelist[i]);
705       if (str == NULL)
706         {
707           /* We don't clean up everything here, but if this failed,
708              something serious happened anyway */
709           for (i = 0; i < filelist_count; i++)
710             {
711               JCL_free (env, filelist[i]);
712             }
713           JCL_free (env, filelist);
714           return 0;
715         }
716
717       /* save into array */
718       (*env)->SetObjectArrayElement (env, filearray, i, str);
719
720       /* delete local reference */
721       (*env)->DeleteLocalRef (env, str);
722     }
723
724   /* free resources */
725   for (i = 0; i < filelist_count; i++)
726     {
727       JCL_free (env, filelist[i]);
728     }
729   JCL_free (env, filelist);
730
731   return filearray;
732 #else /* not WITHOUT_FILESYSTEM */
733   return 0;
734 #endif /* not WITHOUT_FILESYSTEM */
735 }
736
737 /*************************************************************************/
738
739 /*
740  * These two methods are used to maintain dynamically allocated
741  * buffers for getCanonicalPath without the overhead of calling
742  * realloc every time a buffer is modified.  Buffers are sized
743  * at the smallest multiple of CHUNKSIZ that is greater than or
744  * equal to the desired length.  The default CHUNKSIZ is 256,
745  * longer than most paths, so in most cases a getCanonicalPath
746  * will require only one malloc per buffer.
747  */
748
749 #define CHUNKLOG 8
750 #define CHUNKSIZ (1 << CHUNKLOG)
751
752 static int
753 nextChunkSize (int size)
754 {
755   return ((size >> CHUNKLOG) + ((size & (CHUNKSIZ - 1)) ? 1 : 0)) << CHUNKLOG;
756 }
757
758 static char *
759 maybeGrowBuf (JNIEnv *env, char *buf, int *size, int required)
760 {
761   if (required > *size)
762     {
763       *size = nextChunkSize (required);
764       buf = JCL_realloc (env, buf, *size);
765     }
766   return buf;
767 }
768
769 /*************************************************************************/
770
771 /*
772  * This method converts a path to canonical form on GNU/Posix systems.
773  * This involves the removal of redundant separators, references to
774  * "." and "..", and symbolic links.
775  *
776  * The conversion proceeds on a component-by-component basis: symbolic
777  * links and references to ".."  are resolved as and when they occur.
778  * This means that if "/foo/bar" is a symbolic link to "/baz" then the
779  * canonical form of "/foo/bar/.." is "/" and not "/foo".
780  *
781  * In order to mimic the behaviour of proprietary JVMs, non-existant
782  * path components are allowed (a departure from the normal GNU system
783  * convention).  This means that if "/foo/bar" is a symbolic link to
784  * "/baz", the canonical form of "/non-existant-directory/../foo/bar"
785  * is "/baz".
786  *
787  * Class:     java_io_VMFile
788  * Method:    toCanonicalForm
789  * Signature: (Ljava/lang/String)Ljava/lang/String
790  */
791
792 JNIEXPORT jstring JNICALL
793 Java_java_io_VMFile_toCanonicalForm (JNIEnv *env,
794                                      jclass class __attribute__ ((__unused__)),
795                                      jstring jpath)
796 {
797 #ifndef WITHOUT_FILESYSTEM
798   const char *path;
799   char *src, *dst;
800   int srci, dsti;
801   int srcl, dstl;
802   int len;
803   int fschecks;
804 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
805   struct stat sb;
806 #endif /* HAVE_LSTAT && HAVE_READLINK */
807
808   path = JCL_jstring_to_cstring (env, jpath);
809   if (path == NULL)
810     return NULL;
811
812   /* It is the caller's responsibility to ensure the path is absolute. */
813   if (path[0] == 0 || path[0] != '/')
814     {
815       JCL_free_cstring (env, jpath, path);
816       JCL_ThrowException (env, "java/lang/RuntimeException", "Not absolute");
817       return NULL;
818     }
819
820   len = strlen (path);
821   srcl = nextChunkSize (len + 1);
822   src = JCL_malloc (env, srcl);
823   if (src == NULL)
824     {
825       JCL_free_cstring (env, jpath, path);
826       return NULL;
827     }
828   strcpy (src, path);
829   JCL_free_cstring (env, jpath, path);
830   srci = 1;
831
832   dstl = nextChunkSize (2);  
833   dst = JCL_malloc (env, dstl);
834   if (dst == NULL)
835     {
836       JCL_free (env, src);
837       return NULL;
838     }
839   dst[0] = '/';
840   dsti = 1;
841
842   fschecks = JNI_TRUE;
843
844   while (src[srci] != '\0')
845     {
846       int tmpi, dsti_save;
847
848       /* Skip slashes. */
849       while (src[srci] == '/')
850         srci++;
851       tmpi = srci;
852       /* Find next slash. */
853       while (src[srci] != '/' && src[srci] != '\0')
854         srci++;
855       if (srci == tmpi)
856         /* We hit the end. */
857         break;
858       len = srci - tmpi;
859
860       /* Handle "." and "..". */
861       if (len == 1 && src[tmpi] == '.')
862         continue;
863       if (len == 2 && src[tmpi] == '.' && src[tmpi + 1] == '.')
864         {
865           while (dsti > 1 && dst[dsti - 1] != '/')
866             dsti--;
867           if (dsti != 1)
868             dsti--;
869           /* Reenable filesystem checking if disabled, as we might
870            * have reversed over whatever caused the problem before.
871            * At least one proprietary JVM has inconsistencies because
872            * it does not do this.
873            */
874           fschecks = JNI_TRUE;
875           continue;
876         }
877
878       /* Handle real path components. */
879       dst = maybeGrowBuf (env,
880                           dst, &dstl, dsti + (dsti > 1 ? 1 : 0) + len + 1);
881       if (dst == NULL)
882         {
883           JCL_free (env, src);
884           return NULL;
885         }
886       dsti_save = dsti;
887       if (dsti > 1)
888         dst[dsti++] = '/';
889       strncpy (&dst[dsti], &src[tmpi], len);
890       dsti += len;
891       if (fschecks == JNI_FALSE)
892         continue;
893
894 #if defined (HAVE_LSTAT) && defined (HAVE_READLINK)
895       dst[dsti] = '\0';
896       if (lstat (dst, &sb) == 0)
897         {
898           if (S_ISLNK (sb.st_mode))
899             {
900               int tmpl = CHUNKSIZ;
901               char *tmp = JCL_malloc (env, tmpl);
902               if (tmp == NULL)
903                 {
904                   JCL_free (env, src);
905                   JCL_free (env, dst);
906                   return NULL;
907                 }
908
909               while (1)
910                 {
911                   tmpi = readlink (dst, tmp, tmpl);
912                   if (tmpi < 1)
913                     {
914                       JCL_free (env, src);
915                       JCL_free (env, dst);
916                       JCL_free (env, tmp);
917                       JCL_ThrowException (env, "java/io/IOException",
918                                           "readlink failed");
919                       return NULL;
920                     }
921                   if (tmpi < tmpl)
922                     break;
923                   tmpl += CHUNKSIZ;
924                   tmp = JCL_realloc (env, tmp, tmpl);
925                 }
926
927               /* Prepend the link's path to src. */
928               tmp = maybeGrowBuf (env,
929                                   tmp, &tmpl, tmpi + strlen (&src[srci]) + 1);
930               if (tmp == NULL)
931                 {
932                   JCL_free (env, src);
933                   JCL_free (env, dst);
934                   return NULL;
935                 }
936
937               strcpy (&tmp[tmpi], &src[srci]);
938               JCL_free (env, src);
939               src = tmp;
940               srcl = tmpl;
941               srci = 0;
942
943               /* Either replace or append dst depending on whether the
944                * link is relative or absolute.
945                */
946               dsti = src[0] == '/' ? 1 : dsti_save;
947             }
948         }
949       else
950         {
951           /* Something doesn't exist, or we don't have permission to
952            * read it, or a previous path component is a directory, or
953            * a symlink is looped.  Whatever, we can't check the
954            * filesystem any more.
955            */
956           fschecks = JNI_FALSE;
957         }
958 #endif /* HAVE_LSTAT && HAVE_READLINK */
959     }
960   dst[dsti] = '\0';
961
962   jpath = (*env)->NewStringUTF (env, dst);
963   JCL_free (env, src);
964   JCL_free (env, dst);
965   return jpath;
966 #else /* not WITHOUT_FILESYSTEM */
967   return NULL;
968 #endif /* not WITHOUT_FILESYSTEM */
969 }