#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "progname.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <sys/stat.h>
#if HAVE_MACH_O_DYLD_H
# include <mach-o/dyld.h>
#endif
#if defined _WIN32 || defined __WIN32__
# define WIN32_NATIVE
#endif
#if defined WIN32_NATIVE || defined __CYGWIN__
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
#include "xreadlink.h"
#include "canonicalize.h"
#include "relocatable.h"
#ifdef NO_XMALLOC
# define xmalloc malloc
# define xstrdup strdup
#else
# include "xalloc.h"
#endif
#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
# define HAS_DEVICE(P) \
((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
&& (P)[1] == ':')
# define IS_PATH_WITH_DIR(P) \
(strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
#else
# define ISSLASH(C) ((C) == '/')
# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
# define FILE_SYSTEM_PREFIX_LEN(P) 0
#endif
#undef set_program_name
#if ENABLE_RELOCATABLE
#ifdef __linux__
static int executable_fd = -1;
#endif
static bool
maybe_executable (const char *filename)
{
#if !(defined WIN32_NATIVE && !defined __CYGWIN__)
if (access (filename, X_OK) < 0)
return false;
#ifdef __linux__
if (executable_fd >= 0)
{
struct stat statexe;
struct stat statfile;
if (fstat (executable_fd, &statexe) >= 0)
{
if (stat (filename, &statfile) < 0)
return false;
if (!(statfile.st_dev
&& statfile.st_dev == statexe.st_dev
&& statfile.st_ino == statexe.st_ino))
return false;
}
}
#endif
#endif
return true;
}
static char *
find_executable (const char *argv0)
{
#if defined WIN32_NATIVE || defined __CYGWIN__
char location[MAX_PATH];
int length = GetModuleFileName (NULL, location, sizeof (location));
if (length < 0)
return NULL;
if (!IS_PATH_WITH_DIR (location))
return NULL;
{
#if defined __CYGWIN__
static char location_as_posix_path[2 * MAX_PATH];
cygwin_conv_to_posix_path (location, location_as_posix_path);
location_as_posix_path[MAX_PATH - 1] = '\0';
if (strlen (location_as_posix_path) >= MAX_PATH - 1)
return NULL;
return canonicalize_file_name (location_as_posix_path);
#else
return xstrdup (location);
#endif
}
#else
#ifdef __linux__
{
char *link;
link = xreadlink ("/proc/self/exe");
if (link != NULL && link[0] != '[')
return link;
if (executable_fd < 0)
executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
{
char buf[6+10+5];
sprintf (buf, "/proc/%d/exe", getpid ());
link = xreadlink (buf);
if (link != NULL && link[0] != '[')
return link;
if (executable_fd < 0)
executable_fd = open (buf, O_RDONLY, 0);
}
}
#endif
#if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
char location[4096];
unsigned long length = sizeof (location);
if (_NSGetExecutablePath (location, &length) == 0
&& location[0] == '/')
return canonicalize_file_name (location);
#endif
{
bool has_slash = false;
{
const char *p;
for (p = argv0; *p; p++)
if (*p == '/')
{
has_slash = true;
break;
}
}
if (!has_slash)
{
const char *path = getenv ("PATH");
if (path != NULL)
{
const char *p;
const char *p_next;
for (p = path; *p; p = p_next)
{
const char *q;
size_t p_len;
char *concat_name;
for (q = p; *q; q++)
if (*q == ':')
break;
p_len = q - p;
p_next = (*q == '\0' ? q : q + 1);
concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
#ifdef NO_XMALLOC
if (concat_name == NULL)
return NULL;
#endif
if (p_len == 0)
strcpy (concat_name, argv0);
else
{
memcpy (concat_name, p, p_len);
concat_name[p_len] = '/';
strcpy (concat_name + p_len + 1, argv0);
}
if (maybe_executable (concat_name))
return canonicalize_file_name (concat_name);
free (concat_name);
}
}
}
if (maybe_executable (argv0))
return canonicalize_file_name (argv0);
}
return NULL;
#endif
}
static char *executable_fullname;
static void
prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
const char *argv0)
{
const char *curr_prefix;
executable_fullname = find_executable (argv0);
curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
executable_fullname);
if (curr_prefix != NULL)
set_relocation_prefix (orig_installprefix, curr_prefix);
}
void
set_program_name_and_installdir (const char *argv0,
const char *orig_installprefix,
const char *orig_installdir)
{
const char *argv0_stripped = argv0;
{
size_t argv0_len = strlen (argv0);
const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
if (argv0_len > 4 + exeext_len)
if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
{
if (sizeof (EXEEXT) > sizeof (""))
{
static const char exeext[] = EXEEXT;
const char *s1 = argv0 + argv0_len - exeext_len;
const char *s2 = exeext;
for (; *s1 != '\0'; s1++, s2++)
{
unsigned char c1 = *s1;
unsigned char c2 = *s2;
if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
!= (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
goto done_stripping;
}
}
{
char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
#ifdef NO_XMALLOC
if (shorter != NULL)
#endif
{
memcpy (shorter, argv0, argv0_len - exeext_len - 4);
if (sizeof (EXEEXT) > sizeof (""))
memcpy (shorter + argv0_len - exeext_len - 4,
argv0 + argv0_len - exeext_len - 4,
exeext_len);
shorter[argv0_len - 4] = '\0';
argv0_stripped = shorter;
}
}
done_stripping: ;
}
}
set_program_name (argv0_stripped);
prepare_relocate (orig_installprefix, orig_installdir, argv0);
}
char *
get_full_program_name (void)
{
return executable_fullname;
}
#endif