🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Something like GetModuleFileName in Linux?

Started by
10 comments, last by Umesh G Tank 16 years, 10 months ago
Is there a portable way to do the equivalent of GetModuleFileName(0) in Linux? I was working on a game with some friends over the Spring. Now, I'm porting it to Linux, but I've run into a snag. I need to be able to find the directory where the executable is actually located, which I can accomplish easily in Windows with a call to GetModuleFileName(0). In Linux, I thought I could use argv[0] instead, but I've read that this is unreliable at best.
Advertisement
Well I personally would use argv[0].
If argv[0] appears to be a relative path you have to merge it with getcwd() ( your current working directory )

Maybe some of your libraries already provide the functionality for doing paths
or even getting the full executable name,

otherwise you might have to translate the relativ path by hand.

--
Is it really necessary?
There is no portable, reliable way to do that. Just as you wouldn't like a game storing its data in C:\usr\local\games\the-game on Windows, GNU/Linux users find it annoying when a program tries to behave like it's on Windows. There is a filesystem hierarchy, use it. Your application should behave properly, and not try to force the user to accept a behaviour alien to her system.


Quote: Original post by hydroo
Well I personally would use argv[0].
If argv[0] appears to be a relative path you have to merge it with getcwd() ( your current working directory )


$ cat foo.c#include <stdio.h>int main(int argc, char *argv[]) {  printf("%s\n", argv[0]);  return 0;}$ make foo$ /home/username/foo/home/username/foo$ mkdir somedir$ cd somedir$ ln -s ../foo bar$ /home/username/somedir/bar/home/username/somedir/bar


Links are so common on UN*X systems you definitely can't count on them not being used.

There is one way to get such information on GNU/Linux that I know of, but it isn't portable to non-Linux systems (BSD, Solaris), and it requires a /proc filesystem (which a lot of GNU/Linux systems have, but not all do):

#include <stdio.h>#include <sys/types.h>#include <unistd.h>int main(int argc, char *argv[]) {  char path[256];  char id[256];  sprintf(id, "/proc/%d/exe", getpid());  readlink(id, path, 255);  path[255] = '\0';  printf("%s\n", path);  return 0;}


Unstested. You'll want something else than 256 there, and I'm not so sure about readlink(2)'s behaviour wrt the nul character.

Hope this helps.
Boost::FileSystem may have what you need.
Quote: Original post by let_bound
Just as you wouldn't like a game storing its data in C:\usr\local\games\the-game on Windows, GNU/Linux users find it annoying when a program tries to behave like it's on Windows. There is a filesystem hierarchy, use it. Your application should behave properly, and not try to force the user to accept a behaviour alien to her system.


Ideally, I would like the game to be completely apathetic to where it is installed. Are you suggesting that I should avoid assuming that the executable is anywhere near the game's data? (Say, if game data was stored in /usr/local/share and the executable in /usr/local/bin)

I'm already assuming that its OK to write data files like saved games and configuration settings to /home/user-name/.my-game's-name/ If this actually is acceptable, then maybe I'll just ask the user during installation what paths are appropriate and keep them in my setup.ini file.
Quote: Original post by Foxostro
Quote: Original post by let_bound
Just as you wouldn't like a game storing its data in C:\usr\local\games\the-game on Windows, GNU/Linux users find it annoying when a program tries to behave like it's on Windows. There is a filesystem hierarchy, use it. Your application should behave properly, and not try to force the user to accept a behaviour alien to her system.


Ideally, I would like the game to be completely apathetic to where it is installed. Are you suggesting that I should avoid assuming that the executable is anywhere near the game's data? (Say, if game data was stored in /usr/local/share and the executable in /usr/local/bin)


Yes. Game data on UN*X-like systems is typically stored in /usr(/local)/share/games/game_name, while the program is often stored in /usr(/local)/games/game_name, with a symlink in /usr(/local)/bin to the main executable(s). It's also possible to have a non-FHS program in /opt, but I'd like to suggest not going that way unless you have very specific needs (see Wikipedia, keeping in mind that not everything applies... You'll need a bit of GNU/Linux culture here I'm afraid [smile]).


Quote:
I'm already assuming that its OK to write data files like saved games and configuration settings to /home/user-name/.my-game's-name/ If this actually is acceptable, then maybe I'll just ask the user during installation what paths are appropriate and keep them in my setup.ini file.


That's the Right Way to do it, as far as storage is concerned. For your game to locate data, you'll usually do something like this:


  • Your game is free/open source software (FOSS):

    • ./configure --prefix (or any other build system) will be used to determine the path to the executable and the data (it should default to /usr/local, configure does). The binary executables will be in ${prefix}/games/game_name/, the data in ${prefix}/share/games/game_name/, symlink to the executable binary/binaries if you want it/them in ${prefix}/bin/;

    • configure will define the prefix in config.h, so all you need to do is to include config.h in your code to know where to look for the data;

    • package maintainers of the various distros will modify your source code and build system to fit in their distro, and eventually submit their patches, so you don't have to worry about that as long as building your game can be done in a sensible way.



  • Your game is proprietary:

    • I really shouldn't be helping you; [grin]

    • if the user is a lambda user (non-root):

      • have the prefix be /home/username by default (see above for subdirectories), and have the installer install there;

      • allow the user to specify another prefix (make sure it's writable);

      • write the prefix to /home/username/.yourgamerc or /home/username/.your_game/config, or something like that;



    • if the user is root ((e)uid = 0):

      • the prefix should be /usr/local/ by default, but you still want to allow root to specify another prefix;

      • make sure you can write to the directories (things like SELinux can and will deny permission to even root);

      • write the prefix to /etc/your_game/config, or /etc/yourgamerc, or anything you want under /etc (don't overwrite any existing file though);



    • When launched, have you game check for /etc/your_game/config (or /etc/yourgamerc) and /home/username/.your_game/config (or /home/username/.yourgamerc). Allow per-user configuration to override global configuration (useful if a user has installed add-ons, modded maps, or patched things while root was a lazy bastard [grin]).




This is what you're asking for Linux and Darwin, on Linux it uses the /proc filesystem...


#ifdef MAC
std::string get_basepath() {
std::string path = "./";
ProcessSerialNumber PSN;
ProcessInfoRec pinfo;
FSSpec pspec;
FSRef fsr;
OSStatus err;
/* set up process serial number */
PSN.highLongOfPSN = 0;
PSN.lowLongOfPSN = kCurrentProcess;
/* set up info block */
pinfo.processInfoLength = sizeof(pinfo);
pinfo.processName = NULL;
pinfo.processAppSpec = &pspec
/* grab the vrefnum and directory */
err = GetProcessInformation(&PSN, &pinfo);
if (! err ) {
char c_path[2048];
FSSpec fss2;
int tocopy;
err = FSMakeFSSpec(pspec.vRefNum, pspec.parID, 0, &fss2);
if ( ! err ) {
err = FSpMakeFSRef(&fss2, &fsr);
if ( ! err ) {
char c_path[2049];
err = (OSErr)FSRefMakePath(&fsr, (UInt8*)c_path, 2048);
if (! err ) {
path = c_path;
}
}
}
}

return (path);
}
#endif

#ifdef LINUX
std::string get_basepath() {

std::string path = "";
pid_t pid = getpid();
char buf[10];
sprintf(buf,"%d",pid);
std::string _link = "/proc/";
_link.append( buf );
_link.append( "/exe");
char proc[512];
int ch = readlink(_link.c_str(),proc,512);
if (ch != -1) {
proc[ch] = 0;
path = proc;
std::string::size_type t = path.find_last_of("/");
path = path.substr(0,t);
}

return (path);
}
#endif
@benacler: Try wrapping your code in [source][/source] tags to preserve formatting, do highlighting, etcetera. See the forum FAQ.

<hr />
Sander Marechal<small>[Lone Wolves][Hearts for GNOME][E-mail][Forum FAQ]</small>

Hi,

Boost filesystem provides portable way of getting the current file path.

boost::filesystem::path curPath = boost::filesystem::current_path();
std::string strPath = curPath.string();
std::cout<<strPath;

this will give you the current directory path.

hope that it will help you.

Tank
Quote: Original post by Umesh G Tank
Boost filesystem provides portable way of getting the current file path.

boost::filesystem::path curPath = boost::filesystem::current_path();
std::string strPath = curPath.string();
std::cout<<strPath;

this will give you the current directory path.

Which isn't really a solution to Foxostro's problem, since the current path may be a different one from where the executable is located. Boost's filesystem does offer an initial_path function, but it is not guaranteed to work on all platforms (it may just return the current path at the time of its first execution, if I recall correctly).

I recommend following let_bound's advice and taking the few minutes to understand and utilize the at-first disorienting *nix filesystem arrangement.

This topic is closed to new replies.

Advertisement