views:

25540

answers:

11

Is there a platform-agnostic and filesystem-agnostic method to obtain the full path of the directory from where a program is running using C/C++? Not to be confused with the current working directory. (Please don't suggest libraries unless they're standard ones like clib or STL.)

(If there's no platform/filesystem-agnostic method, suggestions that work in Windows and Linux for specific filesystems are welcome too.)

A: 

For Win32 GetCurrentDirectory should do the trick.

Torbjörn Gyllebring
+4  A: 

No, there's no standard way. I believe that the C/C++ standards don't even consider the existence of directories (or other file system organizations).

On Windows the GetModuleFileName() will return the full path to the executable file of the current process when the hModule parameter is set to NULL. I can't help with Linux.

Also you should clarify whether you want the current directory or the directory that the program image/executable resides. As it stands your question is a little ambiguous on this point.

Michael Burr
Thanks for the comment. I have edited the question, I'm interested in the path where the executable resides.
Ashwin
+7  A: 

If you want a standard way without libraries: No. The whole concept of a directory is not included in the standard.

If you agree that some (portable) dependency on a near-standard lib is okay: Use Boost's filesystem library and ask for the initial_path().

IMHO that's as close as you can get, with good karma (Boost is a well-established high quality set of libraries)

Thorsten79
From the Boost docs:template <class Path> const Path Returns: current_path() at the time of entry to main().And current_path() is 'as if by POSIX getcwd()'. This is not what the questioner requested.
Jonathan Leffler
+1  A: 

On POSIX platforms, you can use getcwd().

On Windows, you may use _getcwd(), as use of getcwd() has been deprecated.

For standard libraries, if Boost were standard enough for you, I would have suggested Boost::filesystem, but they seem to have removed path normalization from the proposal. You may have to wait until TR2 becomes readily available for a fully standard solution.

Fruny
getcwd() does not do what the questioner asked.
Jonathan Leffler
+1  A: 

Maybe concatenate the current working directory with argv[0]? I'm not sure if that would work in Windows but it works in linux.

For example:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv) {
    char the_path[256];

    getcwd(the_path, 255);
    strcat(the_path, "/");
    strcat(the_path, argv[0]);

    printf("%s\n", the_path);

    return 0;
}

When run, it outputs:

jeremy@jeremy-desktop:~/Desktop$ ./test
/home/jeremy/Desktop/./test

yjerem
You'll need a check to see if an absolute path is given in argv[0]. But more importantly, what if the image is located via the PATH? Does linux fill in the full path or just what's on the command line?
Michael Burr
As Mike B pointed out, that is a non-general solution; it works in some very limited circumstances only. Basically, only when you run the command by a relative pathname - and it isn't all that elegant when you run ../../../bin/progname instead of ./test
Jonathan Leffler
+4  A: 

You can not use argv[0] for that purpose, usually it does contain full path to the executable, but not nessesarily - process could be created with arbitrary value in the field.

Also mind you, the current directory and the directory with the executable are two different things, so getcwd() won't help you either.

On Windows use GetModuleFileName(), on Linux read /dev/proc/procID/.. files.

eugensk00
A: 

Boost Filesystem's initial_path() behaves like POSIX's getcwd(), and neither does what you want by itself, but appending argv[0] to either of them should do it.

You may note that the result is not always pretty--you may get things like /foo/bar/../../baz/a.out or /foo/bar//baz/a.out, but I believe that it always results in a valid path which names the executable (note that consecutive slashes in a path are collapsed to one).

I previously wrote a solution using envp (the third argument to main() which worked on Linux but didn't seem workable on Windows, so I'm essentially recommending the same solution as someone else did previously, but with the additional explanation of why it is actually correct even if the results are not pretty.

John Zwinck
+7  A: 

getcwd is a POSIX function and supported out of the box by all POSIX compliant platforms. You would not have to do anything special (apart from incliding the right headers unistd.h on Unix and direct.h on windows).

Since you are creating a C program it will link with the default c run time library which is linked to by ALL processes in the system (specially crafted exceptions avoided) and it will include this function by default. The CRT is never considered an external library coz that provides the basic standard compliant interface to the OS.

On windows getcwd function has been depreciated in favour of _getcwd. I think you could use it in this fashion.

#include <stdio.h>  /* defines FILENAME_MAX */
#ifdef WINDOWS
    #include <direct.h>
    #define GetCurrentDir _getcwd
#else
    #include <unistd.h>
    #define GetCurrentDir getcwd
 #endif

 char cCurrentPath[FILENAME_MAX];

 if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
     {
     return errno;
     }

cCurrentPath[sizeof(cCurrentPath) - 1] = '/0'; /* not really required */

printf ("The current working directory is %s", cCurrentPath);
Good answer, but I thought "current working directory" was not what was wanted.
Michael Burr
yet it was somehow accepted...
Frank Szczerba
you should add that even if some documentations say that cCurrentpath can be null and will be allocated by getcwd getcwd does not seem to allocate something on Mac OS and quietly crashes your program
Janusz
why has this been marked as the correct answer when the original poster acknowledges that it's not what they wanted?!
andrew cooke
There is a small error, but unfortunately I can't edit yet.. line 10: cCurrentpath: should be cCurrentPath
Lipis
IMO on Windows the POSIXy-named functions (some of which starting with underscores) should be generally avoided. They aren't the real Windows APIs but rather the CRT. The Windows API you want to use is GetCurrentDirectory(). http://msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
asveikau
+21  A: 

Here's code to get the full path to the executing app:

Windows:

int bytes = GetModuleFileName(NULL, pBuf, len);
if(bytes == 0)
 return -1;
else
 return bytes;

Linux:

char szTmp[32];
sprintf(szTmp, "/proc/%d/exe", getpid());
int bytes = MIN(readlink(szTmp, pBuf, len), len - 1);
if(bytes >= 0)
 pBuf[bytes] = '\0';
return bytes;
I think this is the only answer here that answers the question, and does so for both Windows and Linux. Nice job.
Frank Szczerba
Boo for /proc/pid/exe - Not supported on OS X for some reason.
Chris Lutz
When I see code that looks at `/proc` part of me dies a little. All the world is not Linux, and even on that one platform `/proc` should be considered subject to change from version to version, arch to arch, etc.
asveikau
By the way, a more portable way to get the path to your binary on Unix is to look at `argv[0]`, though this can be forged by the process that launched you.
asveikau
@asveikau `argv[0]` shows the command used to launch the process, so it's only going to show the whole path if the user typed the whole path. If the binary is on their PATH they could just type `foo` to run it, or `./foo` if they're in the current directory -- `argv[0]` would be insufficient either way
Michael Mrozek
@Michael Mrozek - These are cases that are easily detected. Imagine logic which does: if `argv[0]` begins with `'/'`, treat as absolute path, otherwise check if it contains a `'/'`, if so it's a relative path and you need to insert the current working directory, otherwise you need to search for your binary in `$PATH`. As I said, though, this isn't perfect -- I can call `execve()` with `argv[0]` as `"omg ponies"` and the `execve()` will still work.
asveikau
if they launch using an aliased command on Linux is the argv[0] the "name of the command" or expanded?
Andy Dent
A: 

Just to belatedly pile on here,...

there is no standard solution, because the languages are agnostic of underlying file systems, so as others have said, the concept of a directory based file system is outside the scope of the c / c++ languages.

on top of that, you want not the current working directory, but the directory the program is running in, which must take into account how the program got to where it is - ie was it spawned as a new process via a fork, etc. To get the directory a program is running in, as the solutions have demonstrated, requires that you get that information from the process control structures of the operating system in question, which is the only authority on this question. Thus, by definition, its an OS specific solution.

Minok
A: 

As Minok mentioned, there is no such functionality specified ini C standard or C++ standard. This is considered to be purely OS-specific feature and it is specified in POSIX standard, for example.

Thorsten79 has given good suggestion, it is Boost.Filesystem library. However, it may be inconvenient in case you don't want to have any link-time dependencies in binary form for your program.

A good alternative I would recommend is collection of 100% headers-only STLSoft C++ Libraries Matthew Wilson (author of must-read books about C++). There is portable facade PlatformSTL gives access to system-specific API: WinSTL for Windows and UnixSTL on Unix, so it is portable solution. All the system-specific elements are specified with use of traits and policies, so it is extensible framework. There is filesystem library provided, of course.

mloskot