#2761 Fantom File.isDir bug

Ilove:= Fri 19 Jul 2019

Fantom's File.isDir is very unreliable. It's totally rely on backlash to determine if the File is a dir regardless it's actually a directory or not. For example:

//home/user.toFile.isDir will return false even /home/user is actually a dir. //home/user/.toFile.isDir will return true.

//home/user/.bashrc/.toFile.isDir will return true even .bashrc is a file, Fantom should do more check, at least print out an error "do not append backlash after a file name".

//home/user/.bashrc.toFile.isDir will return false.

As you see. It depends on backlash totally and doesn't do any check on the actual file system.

SlimerDude Fri 19 Jul 2019

Just wondering, is that still the case if you normalise the file?

As in:



Ilove:= Fri 19 Jul 2019

Still the same, SlimerDude. It seemed Fan totally depends on backlash do determine if it is a dir or not but doesn't do any real file system check.

Ilove:= Fri 19 Jul 2019

No, SlimerDude. I really think it's a bug in Fantom's implementation. I done a small test with both Java IO and NIO and the result still right, only Fantom got it wrong.

package javatest;

import java.io.File; import java.nio.file.Path; import java.nio.file.Paths;

public class Test {

public static void main(String[] args) {

// TODO Auto-generated method stub
File f = new File("/home/giahung");
Path p = Paths.get("/home/giahung");



brian Fri 19 Jul 2019

The Uri.toFile is a convenience for File(uri, true). The second parameter which defaults to true automatically checks if the file is a directory matches the trailing slash.

However if the file does not exist, there is nothing to check in which case a URI ending is slash is considered a directory and without a slash it is not. Which is consistent with how you want all your relative pathing to work:

fansh> `a/b/` + `file.txt`
fansh> `a/b` + `file.txt`

So I am not sure how unless you explicitly passing false for the checkSlash parameter that the slash doesn't match the actual file/dir in your file system. Furthermore even if you pass false, the File.uri gets normalized correctly. This is what you should see assuming the dir exists:

fansh> `/Users/Brian`.toFile
  sys::IOErr: Must use trailing slash for dir: /Users/Brian
  fan.sys.LocalFile.<init> (LocalFile.java:140)
  fan.sys.File.make (File.java:29)
  fan.sys.File.make (File.java:23)
  fan.sys.Uri.toFile (Uri.java:1356)

Ilove:= Fri 19 Jul 2019

Excuse me I don't understand your post. My situation is "I don't know the file system layout" and I can only probe inside my code. For example, I only know the place my app will work is something /home/user/test and I've to made sure this dir exist and it is a dir but not a normal file, if it is a normal file I will have to create a new dir with name "test" and my app will work inside it (all file, config, data, temp inside this dir).

I only found Fantom's isDir is unreliable to do this job, probing if the file is a dir or not. As I didn't know it is a dir or not how could I know to check with / or without /? Correctly using isDir I should have "already know" it is a dir or not? So what's the point of check? How useless! File.exists only told me the file is exists but nothing about it's a dir or a normal file. Or I've to check both situation with / and without /?

So complicated. Sorry if I confused you. I should just spawn an external process with bash and let [ ] do the probing work. Thanks.

SlimerDude Sat 20 Jul 2019

Following on from Brian's comment, the best way for you to create a File instance is to explicitly use the ctor with false:

testDir := File(`/home/user/test`, false)

If the file exists and it is a dir, then a trailing slash will be automatically added:

testDir := File(`/home/user/test`, false)  // --> /home/user/test/

Meaning you can use the isDir method as usual:

testIsDir := File(`/home/user/test`, false).isDir  // --> true

Ilove:= Sat 20 Jul 2019

I found URI is a mess. The problem is more complicated than I think. The same example of you above, if I use //home/user/test it will return false, but /home/user/test like your code it will return true.

Then I switched back to Windows and do the test with /C:/Users it will return true.

Note: /home/user/test like your code is normal osPath on Linux, on Windows a / has to be added on the beginning and changed all \ to /. To be easier to remember it I added / on the beginning on both platform. It turns out to be not that simple.

I don't know about URI much. Or should I copy/paste the address from Firefox's Open File function: file:///C:/Users , something like that?

I also found another annoying bug. My typo mistake if I test /C:/User which is non-existent at all, it will return false. I think it should throw error immediately, why something doesn't exist considered as "is not a dir"? I'm confused.

Ilove:= Sat 20 Jul 2019

OK. Use file:/// on both platfroms give the same result. I will just copy the uri from firefox's address bar.

SlimerDude Sat 20 Jul 2019

Fully qualified file URIs do look like what Firefox shows. e.g. file:///C:/Users, but you may find it easier to just use the File.os() static method with platform specific notation.

Sometimes even that is messy - so here's a method I use to convert strings (in any format) to File URIs - https://github.com/SlimerDude/afDos/blob/master/fan/DosUtils.fan

My typo mistake if I test /C:/User which is non-existent

You can use File.exists to check if a file exists or not.

I think it should throw error immediately

Very impractical as whether a file exists or not, is entirely dependant on time. There is never any guarantee that a File ever exists. Consider:

file := File(`/file/that/exists.txt`)

Actor.sleep(5sec)        // I can manually delete the file here

text := file.readAllStr  // error - file does not exist

So the only thing you can ever do is check File.exists and that will tell you if the file exists, but only at that specific point in time.

Login or Signup to reply.