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

Get current file name

Moderators: kris

Posted: 09/13/08 00:47:50 Modified: 09/13/08 00:49:45

Tango has functions to get current working directory but does it have anything to get current file name. For example one of the programs i'm making has to load an ini from the same directory as the exe but the exe may not be called from the working directory so I do:

	char[260] thisFile;
	GetModuleFileNameA(null,thisFile.ptr,260);
        auto fp = new FilePath(normalize(thisFile));

but it would be better if i could use tango functions. I probably also need to get this working for linux.

Author Message

Posted: 09/13/08 01:21:57

do you mean the name of the executable, and/or the path involved? If so, that is (by convention) the first element of the array passed to main(char[][] args)

Posted: 09/13/08 05:52:06

No not quiet. If called from command line its just what is typed to call it and when called from command line most users rarely type in the full path so then the app will not know where it's at and won't be able to load any additional files in it's directory.

Have a play with this:

module test;

import tango.io.Console;

int main(char[][] args)
{
	Cout(args[0]).newline;
	Cin.copyln;
	return 0;
}

Posted: 09/13/08 07:44:29

You can use exePath in Environment - if that isn't what you're looking for either, then I don't think there is a direct way in Tango :)

Posted: 09/15/08 11:38:29 -- Modified: 09/15/08 11:40:10 by
D-Crypt -- Modified 2 Times

Hi me again (tim-m89) just created a new account instead of using real name.

No thats not what i mean either. Sometimes programs should know there own path that they are running from rather than working directory which is where they are called from.

module test;

import tango.io.Console;
import tango.sys.Environment;

int main(char[][] args)
{
	auto fp = Environment.exePath("test.exe");
	if(fp is null) Cout("fp is null").newline;
	else Cout("path to test.exe is: "~fp.path).newline;
	Cin.copyln;
	return 0;

}

fp in the above example is null if called from a different directory. Could we have a function to get self executable path. eg:

version (Win32)
{
    static FilePath getSelf()
    {
        char[260] thisFile;
        GetModuleFileNameA(null,thisFile.ptr,260);
        auto fp = new FilePath(normalize(thisFile));
        return fp;
    }
}
else
{
    /* haven't tested this! */
    static FilePath getSelf()
    {
        char[260] thisFile;
        readlink(toStringz("/proc/self/exe"),thisFile.ptr,260);
        auto fp = new FilePath(normalize(thisFile));
        return fp;
    }

}

Posted: 09/15/08 11:49:47

I didn't write the exePath, but if it can be extended to provide more correct information, we'd be interested in including it.

Posted: 09/15/08 12:36:13 -- Modified: 09/15/08 12:36:46 by
D-Crypt

larsivi wrote:

I didn't write the exePath, but if it can be extended to provide more correct information, we'd be interested in including it.

Not that I have extensively tested it but I didn't think there was a problem with it. I'm assuming exe path checks in current directory then in your path environmental variables for the specified program. A little different to getting the full path of your self.

Posted: 09/15/08 17:08:50

There is a problem in your code, you should use args[0], not the name of the executable. This is because the args[0] is equivalent to what you typed to run the program on the command line. Environment.exePath should work in that case.

Posted: 09/15/08 23:08:19

schveiguy wrote:

There is a problem in your code, you should use args[0], not the name of the executable. This is because the args[0] is equivalent to what you typed to run the program on the command line. Environment.exePath should work in that case.

Look at post # 3. The code there. Compile it. Go back up one directory. Then type in something like test-cmd\test.exe (if using windows) and that is exactly what args[0] is set to "test-cmd\test.exe". The program still doesn't know where it's at. Fail.

Posted: 09/16/08 04:39:47

Seems like a good thing to have ... is there any way you can test it on linux, D-Crypt?

Posted: 09/16/08 09:42:39

OK i have tested it now. The code works like this:

version (Win32)
{
    static FilePath getSelf()
    {
        char[] thisFile = new char[1024];
        GetModuleFileNameA(null,thisFile.ptr,1024);
        auto fp = new FilePath(normalize(thisFile));
        return fp;
    }
}
else
{
    static FilePath getSelf()
    {
        char[] thisFile = new char[1024];
        thisFile = thisFile[0..(readlink(toStringz("/proc/self/exe"),thisFile.ptr,1024))];
        auto fp = new FilePath(normalize(thisFile));
        return fp;
    }
}

Not sure what's best for the lengths though. Keep increasing it until you get a string that's shorter than what you ask for or pick a high enough number to begin with? Also the w32 code requires tango.sys.win32.UserGdi? and the posix code requires tango.stdc.posix.unistd.

Posted: 09/16/08 14:51:35

D-Crypt wrote:

Seems to work for me:

module test;

import tango.io.Console;
import tango.sys.Environment;

int main(char[][] args)
{
	auto fp = Environment.exePath(args[0]);
	if(fp is null) Cout("fp is null").newline;
	else Cout("path to this program is: "~fp.path).newline;
	Cin.copyln;
	return 0;
}
C:\blah\test-cmd>dmd testit.d

C:\blah\test-cmd>testit.exe
path to this program is: C:/blah/test-cmd/


C:\blah\test-cmd>cd ..

C:\blah>test-cmd\testit.exe
path to this program is: C:/blah/test-cmd/


C:\blah>test-cmd\testit
path to this program is: C:/blah/test-cmd/


C:\blah>

Posted: 09/17/08 08:45:15 -- Modified: 09/17/08 10:32:24 by
D-Crypt

OK didn't expect that to work like that. However I still prefer to use the other code as the windows api and posix /proc exposes that certain functionality for that specific reason. Also if you type in ../../.. etc.. you get returned those values which works but looks messy. If its a symbolic link then that is also what you get using your way. A program may only have a sym link for the program itself so trying to do it your way would fail at loading in its additional files unless you try to de-reference each symlink using readlink. Much easier to readlink /proc/self/exe so I still stand by my point.

Posted: 09/17/08 12:58:54

Yes, it would be a valid addition to the library. I just was rebutting your assertion that exePath was broken if you didn't have the executable in your path :) Having related config files in the same directory as the exe is not a common solution for unix-based programs, usually the config is in /etc or in a home .progname directory.

In order for your solution to work, you need a Mac (and soon freeBSD) solution as well (I don't know if they have /proc, but I thought that was a linux-specific thing).

Posted: 03/15/09 18:10:04

Has D-Crypt's solution gotten into Tango?

schveiguy wrote:

Having related config files in the same directory as the exe is not a common solution for unix-based programs, usually the config is in /etc or in a home .progname directory.

Is that also true of a program's data/resource files?

Posted: 03/15/09 21:15:10

I also need this so here is the mac solution:

import tango.stdc.config;

alias c_long CFIndex;
alias void** CFAllocatorRef;
alias void** CFURLRef;
alias void** CFStringRef;
alias void* CFTypeRef;

enum CFURLPathStyle
{
	kCFURLPOSIXPathStyle = 0,
	kCFURLHFSPathStyle = 1,
	kCFURLWindowsPathStyle = 2
}

enum CFStringEncoding
{
	kCFStringEncodingMacRoman = 0,
	kCFStringEncodingWindowsLatin1 = 0x0500,
	kCFStringEncodingISOLatin1 = 0x0201,
	kCFStringEncodingNextStepLatin = 0x0B01,
	kCFStringEncodingASCII = 0x0600,
	kCFStringEncodingUnicode = 0x0100,
	kCFStringEncodingUTF8 = 0x08000100,
	kCFStringEncodingNonLossyASCII = 0x0BFF,

	kCFStringEncodingUTF16 = 0x0100,
	kCFStringEncodingUTF16BE = 0x10000100,
	kCFStringEncodingUTF16LE = 0x14000100,
	kCFStringEncodingUTF32 = 0x0c000100,
	kCFStringEncodingUTF32BE = 0x18000100,
	kCFStringEncodingUTF32LE = 0x1c000100
}

struct ProcessSerialNumber 
{
    uint highLongOfPSN;
    uint lowLongOfPSN;
}

struct FSRef
{
	ubyte hidden[80];
}

extern (C)
{	
	extern const CFAllocatorRef kCFAllocatorDefault;
	
	CFIndex CFStringGetLength (CFStringRef theString);
	void CFRelease (CFTypeRef cf);
	bool CFStringGetCString (CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding);
	
	CFURLRef CFURLCreateFromFSRef (CFAllocatorRef allocator, FSRef* fsRef);
	CFStringRef CFURLCopyFileSystemPath (CFURLRef anURL, CFURLPathStyle pathStyle);
	
	short GetCurrentProcess (ProcessSerialNumber* PSN);
	int GetProcessBundleLocation (ProcessSerialNumber* psn, FSRef* location);
}

char[] getPath ()
{
	ProcessSerialNumber psn;	
	FSRef fs;
	CFURLRef url;
	CFStringRef cfString;
	CFIndex length_;
	
	GetCurrentProcess(&psn);
	GetProcessBundleLocation(&psn, &fs);
	url = CFURLCreateFromFSRef(kCFAllocatorDefault, &fs);
	
	scope (exit)
		CFRelease(url);
	
	if (url !is null)
		cfString = CFURLCopyFileSystemPath(url, CFURLPathStyle.kCFURLPOSIXPathStyle);
	
	length_ = CFStringGetLength(cfString);
	char[] buf = new char[length_ + 1];
	CFStringGetCString(cfString, buf.ptr, buf.length, CFStringEncoding.kCFStringEncodingUTF8);
	
	return buf[0 .. $ - 1];
}

You have to link to the Carbon framework for this to work.

Posted: 03/18/09 13:41:54

I've created ticket #1536 for this with implementations for: darwin, freebsd, linux and windows.

Posted: 03/18/09 15:28:24

Abscissa wrote:
schveiguy wrote:

Having related config files in the same directory as the exe is not a common solution for unix-based programs, usually the config is in /etc or in a home .progname directory.

Is that also true of a program's data/resource files?

it's different for data files, but they still don't go in the same directory as the executable. I'm not sure of the normal convention, but some programs put them into a /var/lib/progname or /usr/share/progname directory. Usually executables go into /bin, /sbin, /usr/bin, or /usr/sbin.