This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Path To Executable On Linux Submitted by

If you want to release self-contained games for Linux, where both the executable and the game data files are in the same directory, you somehow need a way to find the path to the game data (the current working directory isn't reliable when the game is launched from the desktop, for example). The attached function can reliably retrieve the path to the currently running game executable. Once you have this path, you're just a few simple string operations away from the absolute paths to the game data files. cu,
Nicolai

Download Associated File: getexename.c (5,983 bytes)

    /*
     * getexename.c
     *
     * written by Nicolai Haehnle <prefect_@gmx.net>
     * I hereby release this trivial piece of code to the public domain.
     *
     * The function getexename() returns the filename of the currently loaded
     * executable.
     *
     * Intended use of this function is to facilitate easier packaging of
     * third-party software for the Linux operating system. The FHS mandates
     * that files that belong to one package are scattered throughout the
     * file system. This works as long as packages are maintained by a
     * package management program. However, it is impossible for application
     * developers to provide packages for every Linux distribution out there.
     * Finding the file locations is also difficult when an application is
     * installed locally by a user inside her own home directory.
     *
     * The simplest and most straight-forward solution to this problem is to
     * put all files belonging to a package into the same directory. The program
     * executable can then reference the necessary data files by using paths
     * relative to the executable location.
     * To give an example:
     *
     *  A simple game, consisting of an executable and a number of data files
     *  (e.g. images), resides entirely in one directory, with absolute filenames
     *  like this:
     *    /the/path/foogame
     *    /the/path/images/hero.png
     *    /the/path/images/badass.png
     *  The game executable can use getexename() to find its own location, strip
     *  off the last component to get the directory the executable is located in,
     *  and append the relative paths "images/hero.png" and "images/badass.png"
     *  to reference the data files.
     *  The game will be completely position independent. The user is free to
     *  move it somewhere else in the filesystem, and it will just work; it will
     *  no longer be necessary to change configuration files or even recompile the
     *  executable.
     *
     * If you are concerned about executables showing up in a user's PATH, you
     * should somehow arrange for symlinks to be made. For example, if
     * /usr/games/foogame is a symlink to /the/path/foogame, the user can run the
     * game simply by typing "foogame" in the shell (provided that /usr/games is in
     * the user's PATH); since symlinks cannot fool getexename(), the game will
     * still work. (Do note that a hard link will defeat getexename()).
     *
     * Note that while it is possible to reference data files based on the current
     * working directory, this technique only works if the user explicitly sets
     * the CWD to the application's base directory. Therefore, using the executable
     * name as a base is more robust.
     *
     * Also note that while argv[0] can be used as the executable name in many
     * cases as well, it is easily fooled by symlinks and may not contain an
     * absolute filename. argv[0] can also be set to something entirely different
     * from the executable filename by the executing process, either delibaretly
     * or by invoking scripts.
     *
     * Note that this function relies on the layout of the /proc file system, so
     * portability is an issue. While I assume that this part of /proc is fairly
     * stable, I have no documentation whatsoever about potential differences
     * between Linux kernel versions in this area.
     *
     */

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>

    #include <sys/types.h>
    #include <unistd.h>

    /*
     * getexename - Get the filename of the currently running executable
     *
     * The getexename() function copies an absolute filename of the currently
     * running executable to the array pointed to by buf, which is of length size.
     *
     * If the filename would require a buffer longer than size elements, NULL is
     * returned, and errno is set to ERANGE; an application should check for this
     * error, and allocate a larger buffer if necessary.
     *
     * Return value:
     * NULL on failure, with errno set accordingly, and buf on success. The
     * contents of the array pointed to by buf is undefined on error.
     *
     * Notes:
     * This function is tested on Linux only. It relies on information supplied by
     * the /proc file system.
     * The returned filename points to the final executable loaded by the execve()
     * system call. In the case of scripts, the filename points to the script
     * handler, not to the script.
     * The filename returned points to the actual exectuable and not a symlink.
     *
     */
    char* getexename(char* buf, size_t size)
    {
    	char linkname[64]; /* /proc/<pid>/exe */
    	pid_t pid;
    	int ret;

    	/* Get our PID and build the name of the link in /proc */
    	pid = getpid();

    	if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid) < 0)
    		{
    		/* This should only happen on large word systems. I'm not sure
    		   what the proper response is here.
    		   Since it really is an assert-like condition, aborting the
    		   program seems to be in order. */
    		abort();
    		}

    	/* Now read the symbolic link */
    	ret = readlink(linkname, buf, size);

    	/* In case of an error, leave the handling up to the caller */
    	if (ret == -1)
    		return NULL;

    	/* Report insufficient buffer size */
    	if (ret >= size)
    		{
    		errno = ERANGE;
    		return NULL;
    		}

    	/* Ensure proper NUL termination */
    	buf[ret] = 0;

    	return buf;
    }

    #if 1 /* testing */

    /*
     * Trivial test and sample use of getexename().
     */
    int main()
    {
    	char* buf;
    	int size;

    	buf = NULL;
    	size = 32; /* Set an initial size estimate */

    	for(;;)
    		{
    		char* res;

    		/* Allocate and fill the buffer */
    		buf = (char*)malloc(size);
    		res = getexename(buf, size);

    		/* Get out of the loop on success */
    		if (res)
    			break;

    		/* Anything but ERANGE indicates a real error */
    		if (errno != ERANGE)
    			{
    			perror("getexename() failed");
    			free(buf);
    			buf = NULL;
    			break;
    			}

    		/* ERANGE means the buffer was too small. Free the current
    		   buffer and retry with a bigger one. */
    		free(buf);
    		size *= 2;
    		}

    	/* Exit on failure */
    	if (buf == NULL)
    		return -1;

    	/* Now print the executable name. */
    	printf("getexename(): %s\n", buf);
    	free(buf);

    	return 0; /* Indicate success */
    }

    #endif /* testing */

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.