Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Issues with tango.io.FilePath

Moderators: kris

Posted: 02/09/07 11:10:53

After deciding to port my config file parser to Tango, I quickly became annoyed at the state of Tango's path handling abilities. For instance, I have a function of the following form:

char[] same_dir(char[] arg0, char[] filename);

arg0 is intended to be the first element of the args array which is passed to main(). filename should be the name of a file. The function returns a complete path which would place filename next to the executable.

Phobos behaves differently in Windows and in Linux with respect to arg0. Windows always receives the absolute path of the executable, and Linux gets the exact string used to start the program. This means that it can be necessary to search the PATH on Linux to find the absolute path to the binary.

The Phobos version of this function looks like this:

import std.string : split;
import std.file : exists;
import std.path : getBaseName, getDirName, join, pathsep;

// Wraps std.c.stdlib.getenv
char[] env(char* var);

char[] same_dir(char[] arg0, char[] filename) {
    char[] dir = getDirName(arg0);
    if (dir == "") {
        char[] bin_name = getBaseName(arg0);
        char[][] paths = split(env("PATH"), pathsep);
        foreach (path; paths) {
            if (exists(join(path, bin_name)))
                return join(path, filename);
        }
        // This should probably throw something...
        return "";
    } else {
        return join(dir, filename);
    }
}

The Tango version is not as pretty. This uses Tango beta1; some of these portions of the Tango API have changed in SVN since then. One thing of note with Tango is it behaves on both Windows and Linux how Phobos behaves only on Linux: It sends the exact string used to start the program to arg0. This can cause issues on Windows: Not only is there no way to distinguish calling a program in the CWD from one on the PATH (meaning I have to add "." to the PATH list in the below code), but it's possible to elide the .exe extension from the filename. I'd probably call these issues with Windows rather than with Tango, but Phobos's behavior neatly sidesteps them.

import tango.io.FilePath : FilePath;
import tango.io.FileProxy : FileProxy;
import tango.text.Util : locatePrior, demarcate;
version (Windows) {
    const char[] SystemPathSeparator = ";";
} else {
    const char[] SystemPathSeparator = ":";
}

// Wraps tango.stdc.stdlib.getenv
char[] env(char* var);

char[] same_dir(char[] arg0, char[] filename) {
    scope binary = new FilePath(arg0);
    scope file = new FilePath(filename);
    char[] dir = binary.getPath();
    char[] bin = binary.getFullName();
    version (Windows) {
        if (bin[$-4 .. $] != ".exe")
            bin ~= ".exe";
    }
    scope bin_name = new FilePath(bin);
    if (dir == "") {
        char[][] paths = demarcate(env("PATH"), SystemPathSeparator);
        version(Windows) paths = ["."] ~ paths;
        foreach (path; paths) {
            scope temp = new FileProxy(bin_name.join(path));
            if (temp.isExisting())
                return file.join(path).toUtf8();
        }
        // This should probably throw something...
        return "";
    } else {
        return file.join(dir).toUtf8();
    }
}

Although I've declared all of the class instances as being 'scope' instances, this still makes me uncomfortable. The fact that I have to create class instances at all to do these simple path operations annoys me.

Author Message

Posted: 02/09/07 19:13:15 -- Modified: 02/09/07 19:16:40 by
kris

It's good to see this listed in full, but I'm just a little surprised given that the changes you asked for were discussed, checked in, and communicated to you long before the example here was posted. Can you perhaps provide an example with the changes, as you had asked for them? Thanks :)

Posted: 02/18/07 08:01:06 -- Modified: 03/03/07 19:42:46 by
kris -- Modified 2 Times

Here's a recent Tango equivalent:

import tango.io.FilePath;
import tango.sys.Environment;
import Text = tango.text.Util;

char[] same_dir(char[] arg0, char[] filename) 
{
    auto bin = new FilePath (arg0);

    version (Win32) 
             if (bin.ext.length is 0)
                 bin.append (".exe");

    if (bin.folder.length) 
        return bin.file(filename).toUtf8;
    else 
       foreach (testPath; Text.patterns (getEnv("PATH"), FileConst.SystemPathString)) 
                if (bin.path(testPath).exists)
                    return bin.file(filename).toUtf8;
    return null;
}