Blame gettext-tools/gnulib-lib/progreloc.c

Packit Bot 06c835
/* Provide relocatable programs.
Packit Bot 06c835
   Copyright (C) 2003-2015 Free Software Foundation, Inc.
Packit Bot 06c835
   Written by Bruno Haible <bruno@clisp.org>, 2003.
Packit Bot 06c835
Packit Bot 06c835
   This program is free software: you can redistribute it and/or modify
Packit Bot 06c835
   it under the terms of the GNU General Public License as published by
Packit Bot 06c835
   the Free Software Foundation; either version 3 of the License, or
Packit Bot 06c835
   (at your option) any later version.
Packit Bot 06c835
Packit Bot 06c835
   This program is distributed in the hope that it will be useful,
Packit Bot 06c835
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Bot 06c835
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Bot 06c835
   GNU General Public License for more details.
Packit Bot 06c835
Packit Bot 06c835
   You should have received a copy of the GNU General Public License
Packit Bot 06c835
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Bot 06c835
Packit Bot 06c835
Packit Bot 06c835
#define _GL_USE_STDLIB_ALLOC 1
Packit Bot 06c835
#include <config.h>
Packit Bot 06c835
Packit Bot 06c835
/* Specification.  */
Packit Bot 06c835
#include "progname.h"
Packit Bot 06c835
Packit Bot 06c835
#include <stdbool.h>
Packit Bot 06c835
#include <stdio.h>
Packit Bot 06c835
#include <stdlib.h>
Packit Bot 06c835
#include <string.h>
Packit Bot 06c835
#include <fcntl.h>
Packit Bot 06c835
#include <unistd.h>
Packit Bot 06c835
#include <sys/stat.h>
Packit Bot 06c835
Packit Bot 06c835
/* Get declaration of _NSGetExecutablePath on Mac OS X 10.2 or newer.  */
Packit Bot 06c835
#if HAVE_MACH_O_DYLD_H
Packit Bot 06c835
# include <mach-o/dyld.h>
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
Packit Bot 06c835
# define WINDOWS_NATIVE
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
#ifdef WINDOWS_NATIVE
Packit Bot 06c835
# define WIN32_LEAN_AND_MEAN
Packit Bot 06c835
# include <windows.h>
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
#ifdef __EMX__
Packit Bot 06c835
# define INCL_DOS
Packit Bot 06c835
# include <os2.h>
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
#include "relocatable.h"
Packit Bot 06c835
Packit Bot 06c835
#ifdef NO_XMALLOC
Packit Bot 06c835
# include "areadlink.h"
Packit Bot 06c835
# define xreadlink areadlink
Packit Bot 06c835
#else
Packit Bot 06c835
# include "xreadlink.h"
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
#ifdef NO_XMALLOC
Packit Bot 06c835
# define xmalloc malloc
Packit Bot 06c835
# define xstrdup strdup
Packit Bot 06c835
#else
Packit Bot 06c835
# include "xalloc.h"
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
#ifndef O_EXEC
Packit Bot 06c835
# define O_EXEC O_RDONLY /* This is often close enough in older systems.  */
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
/* Declare canonicalize_file_name.
Packit Bot 06c835
   The <stdlib.h> included above may be the system's one, not the gnulib
Packit Bot 06c835
   one.  */
Packit Bot 06c835
extern char * canonicalize_file_name (const char *name);
Packit Bot 06c835
Packit Bot 06c835
/* Pathname support.
Packit Bot 06c835
   ISSLASH(C)           tests whether C is a directory separator character.
Packit Bot 06c835
   IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
Packit Bot 06c835
 */
Packit Bot 06c835
#if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
Packit Bot 06c835
  /* Native Windows, OS/2, DOS */
Packit Bot 06c835
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
Packit Bot 06c835
# define HAS_DEVICE(P) \
Packit Bot 06c835
    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
Packit Bot 06c835
     && (P)[1] == ':')
Packit Bot 06c835
# define IS_PATH_WITH_DIR(P) \
Packit Bot 06c835
    (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
Packit Bot 06c835
# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
Packit Bot 06c835
#else
Packit Bot 06c835
  /* Unix */
Packit Bot 06c835
# define ISSLASH(C) ((C) == '/')
Packit Bot 06c835
# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
Packit Bot 06c835
# define FILE_SYSTEM_PREFIX_LEN(P) 0
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
/* Use the system functions, not the gnulib overrides in this file.  */
Packit Bot 06c835
#undef sprintf
Packit Bot 06c835
Packit Bot 06c835
#undef set_program_name
Packit Bot 06c835
Packit Bot 06c835
Packit Bot 06c835
#if ENABLE_RELOCATABLE
Packit Bot 06c835
Packit Bot 06c835
#if defined __linux__ || defined __CYGWIN__
Packit Bot 06c835
/* File descriptor of the executable.
Packit Bot 06c835
   (Only used to verify that we find the correct executable.)  */
Packit Bot 06c835
static int executable_fd = -1;
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
/* Tests whether a given pathname may belong to the executable.  */
Packit Bot 06c835
static bool
Packit Bot 06c835
maybe_executable (const char *filename)
Packit Bot 06c835
{
Packit Bot 06c835
  /* The native Windows API lacks the access() function.  */
Packit Bot 06c835
#if !defined WINDOWS_NATIVE
Packit Bot 06c835
  if (access (filename, X_OK) < 0)
Packit Bot 06c835
    return false;
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
#if defined __linux__ || defined __CYGWIN__
Packit Bot 06c835
  if (executable_fd >= 0)
Packit Bot 06c835
    {
Packit Bot 06c835
      /* If we already have an executable_fd, check that filename points to
Packit Bot 06c835
         the same inode.  */
Packit Bot 06c835
      struct stat statexe;
Packit Bot 06c835
      struct stat statfile;
Packit Bot 06c835
Packit Bot 06c835
      if (fstat (executable_fd, &statexe) >= 0)
Packit Bot 06c835
        {
Packit Bot 06c835
          if (stat (filename, &statfile) < 0)
Packit Bot 06c835
            return false;
Packit Bot 06c835
          if (!(statfile.st_dev
Packit Bot 06c835
                && statfile.st_dev == statexe.st_dev
Packit Bot 06c835
                && statfile.st_ino == statexe.st_ino))
Packit Bot 06c835
            return false;
Packit Bot 06c835
        }
Packit Bot 06c835
    }
Packit Bot 06c835
#endif
Packit Bot 06c835
Packit Bot 06c835
  return true;
Packit Bot 06c835
}
Packit Bot 06c835
Packit Bot 06c835
/* Determine the full pathname of the current executable, freshly allocated.
Packit Bot 06c835
   Return NULL if unknown.
Packit Bot 06c835
   Guaranteed to work on Linux and native Windows.  Likely to work on the
Packit Bot 06c835
   other Unixes (maybe except BeOS), under most conditions.  */
Packit Bot 06c835
static char *
Packit Bot 06c835
find_executable (const char *argv0)
Packit Bot 06c835
{
Packit Bot 06c835
#if defined WINDOWS_NATIVE
Packit Bot 06c835
  /* Native Windows only.
Packit Bot 06c835
     On Cygwin, it is better to use the Cygwin provided /proc interface, than
Packit Bot 06c835
     to use native Windows API and cygwin_conv_to_posix_path, because it
Packit Bot 06c835
     supports longer file names
Packit Bot 06c835
     (see <http://cygwin.com/ml/cygwin/2011-01/msg00410.html>).  */
Packit Bot 06c835
  char location[MAX_PATH];
Packit Bot 06c835
  int length = GetModuleFileName (NULL, location, sizeof (location));
Packit Bot 06c835
  if (length < 0)
Packit Bot 06c835
    return NULL;
Packit Bot 06c835
  if (!IS_PATH_WITH_DIR (location))
Packit Bot 06c835
    /* Shouldn't happen.  */
Packit Bot 06c835
    return NULL;
Packit Bot 06c835
  return xstrdup (location);
Packit Bot 06c835
#elif defined __EMX__
Packit Bot 06c835
  PPIB ppib;
Packit Bot 06c835
  char location[CCHMAXPATH];
Packit Bot 06c835
Packit Bot 06c835
  /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/619_L2H_DosGetInfoBlocksSynt.html
Packit Bot 06c835
     for specification of DosGetInfoBlocks().  */
Packit Bot 06c835
  if (DosGetInfoBlocks (NULL, &ppib))
Packit Bot 06c835
    return NULL;
Packit Bot 06c835
Packit Bot 06c835
  /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
Packit Bot 06c835
     for specification of DosQueryModuleName().  */
Packit Bot 06c835
  if (DosQueryModuleName (ppib->pib_hmte, sizeof (location), location))
Packit Bot 06c835
    return NULL;
Packit Bot 06c835
Packit Bot 06c835
  _fnslashify (location);
Packit Bot 06c835
Packit Bot 06c835
  return xstrdup (location);
Packit Bot 06c835
#else /* Unix */
Packit Bot 06c835
# ifdef __linux__
Packit Bot 06c835
  /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
Packit Bot 06c835
     versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
Packit Bot 06c835
     to the true pathname; older Linux versions give only device and ino,
Packit Bot 06c835
     enclosed in brackets, which we cannot use here.  */
Packit Bot 06c835
  {
Packit Bot 06c835
    char *link;
Packit Bot 06c835
Packit Bot 06c835
    link = xreadlink ("/proc/self/exe");
Packit Bot 06c835
    if (link != NULL && link[0] != '[')
Packit Bot 06c835
      return link;
Packit Bot 06c835
    if (executable_fd < 0)
Packit Bot 06c835
      executable_fd = open ("/proc/self/exe", O_EXEC, 0);
Packit Bot 06c835
Packit Bot 06c835
    {
Packit Bot 06c835
      char buf[6+10+5];
Packit Bot 06c835
      sprintf (buf, "/proc/%d/exe", getpid ());
Packit Bot 06c835
      link = xreadlink (buf);
Packit Bot 06c835
      if (link != NULL && link[0] != '[')
Packit Bot 06c835
        return link;
Packit Bot 06c835
      if (executable_fd < 0)
Packit Bot 06c835
        executable_fd = open (buf, O_EXEC, 0);
Packit Bot 06c835
    }
Packit Bot 06c835
  }
Packit Bot 06c835
# endif
Packit Bot 06c835
# ifdef __CYGWIN__
Packit Bot 06c835
  /* The executable is accessible as /proc/<pid>/exe, at least in
Packit Bot 06c835
     Cygwin >= 1.5.  */
Packit Bot 06c835
  {
Packit Bot 06c835
    char *link;
Packit Bot 06c835
Packit Bot 06c835
    link = xreadlink ("/proc/self/exe");
Packit Bot 06c835
    if (link != NULL)
Packit Bot 06c835
      return link;
Packit Bot 06c835
    if (executable_fd < 0)
Packit Bot 06c835
      executable_fd = open ("/proc/self/exe", O_EXEC, 0);
Packit Bot 06c835
  }
Packit Bot 06c835
# endif
Packit Bot 06c835
# if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
Packit Bot 06c835
  /* On Mac OS X 10.2 or newer, the function
Packit Bot 06c835
       int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
Packit Bot 06c835
     can be used to retrieve the executable's full path.  */
Packit Bot 06c835
  char location[4096];
Packit Bot 06c835
  unsigned int length = sizeof (location);
Packit Bot 06c835
  if (_NSGetExecutablePath (location, &length) == 0
Packit Bot 06c835
      && location[0] == '/')
Packit Bot 06c835
    return canonicalize_file_name (location);
Packit Bot 06c835
# endif
Packit Bot 06c835
  /* Guess the executable's full path.  We assume the executable has been
Packit Bot 06c835
     called via execlp() or execvp() with properly set up argv[0].  The
Packit Bot 06c835
     login(1) convention to add a '-' prefix to argv[0] is not supported.  */
Packit Bot 06c835
  {
Packit Bot 06c835
    bool has_slash = false;
Packit Bot 06c835
    {
Packit Bot 06c835
      const char *p;
Packit Bot 06c835
      for (p = argv0; *p; p++)
Packit Bot 06c835
        if (*p == '/')
Packit Bot 06c835
          {
Packit Bot 06c835
            has_slash = true;
Packit Bot 06c835
            break;
Packit Bot 06c835
          }
Packit Bot 06c835
    }
Packit Bot 06c835
    if (!has_slash)
Packit Bot 06c835
      {
Packit Bot 06c835
        /* exec searches paths without slashes in the directory list given
Packit Bot 06c835
           by $PATH.  */
Packit Bot 06c835
        const char *path = getenv ("PATH");
Packit Bot 06c835
Packit Bot 06c835
        if (path != NULL)
Packit Bot 06c835
          {
Packit Bot 06c835
            const char *p;
Packit Bot 06c835
            const char *p_next;
Packit Bot 06c835
Packit Bot 06c835
            for (p = path; *p; p = p_next)
Packit Bot 06c835
              {
Packit Bot 06c835
                const char *q;
Packit Bot 06c835
                size_t p_len;
Packit Bot 06c835
                char *concat_name;
Packit Bot 06c835
Packit Bot 06c835
                for (q = p; *q; q++)
Packit Bot 06c835
                  if (*q == ':')
Packit Bot 06c835
                    break;
Packit Bot 06c835
                p_len = q - p;
Packit Bot 06c835
                p_next = (*q == '\0' ? q : q + 1);
Packit Bot 06c835
Packit Bot 06c835
                /* We have a path item at p, of length p_len.
Packit Bot 06c835
                   Now concatenate the path item and argv0.  */
Packit Bot 06c835
                concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
Packit Bot 06c835
# ifdef NO_XMALLOC
Packit Bot 06c835
                if (concat_name == NULL)
Packit Bot 06c835
                  return NULL;
Packit Bot 06c835
# endif
Packit Bot 06c835
                if (p_len == 0)
Packit Bot 06c835
                  /* An empty PATH element designates the current directory.  */
Packit Bot 06c835
                  strcpy (concat_name, argv0);
Packit Bot 06c835
                else
Packit Bot 06c835
                  {
Packit Bot 06c835
                    memcpy (concat_name, p, p_len);
Packit Bot 06c835
                    concat_name[p_len] = '/';
Packit Bot 06c835
                    strcpy (concat_name + p_len + 1, argv0);
Packit Bot 06c835
                  }
Packit Bot 06c835
                if (maybe_executable (concat_name))
Packit Bot 06c835
                  return canonicalize_file_name (concat_name);
Packit Bot 06c835
                free (concat_name);
Packit Bot 06c835
              }
Packit Bot 06c835
          }
Packit Bot 06c835
        /* Not found in the PATH, assume the current directory.  */
Packit Bot 06c835
      }
Packit Bot 06c835
    /* exec treats paths containing slashes as relative to the current
Packit Bot 06c835
       directory.  */
Packit Bot 06c835
    if (maybe_executable (argv0))
Packit Bot 06c835
      return canonicalize_file_name (argv0);
Packit Bot 06c835
  }
Packit Bot 06c835
  /* No way to find the executable.  */
Packit Bot 06c835
  return NULL;
Packit Bot 06c835
#endif
Packit Bot 06c835
}
Packit Bot 06c835
Packit Bot 06c835
/* Full pathname of executable, or NULL.  */
Packit Bot 06c835
static char *executable_fullname;
Packit Bot 06c835
Packit Bot 06c835
static void
Packit Bot 06c835
prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
Packit Bot 06c835
                  const char *argv0)
Packit Bot 06c835
{
Packit Bot 06c835
  char *curr_prefix;
Packit Bot 06c835
Packit Bot 06c835
  /* Determine the full pathname of the current executable.  */
Packit Bot 06c835
  executable_fullname = find_executable (argv0);
Packit Bot 06c835
Packit Bot 06c835
  /* Determine the current installation prefix from it.  */
Packit Bot 06c835
  curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
Packit Bot 06c835
                                     executable_fullname);
Packit Bot 06c835
  if (curr_prefix != NULL)
Packit Bot 06c835
    {
Packit Bot 06c835
      /* Now pass this prefix to all copies of the relocate.c source file.  */
Packit Bot 06c835
      set_relocation_prefix (orig_installprefix, curr_prefix);
Packit Bot 06c835
Packit Bot 06c835
      free (curr_prefix);
Packit Bot 06c835
    }
Packit Bot 06c835
}
Packit Bot 06c835
Packit Bot 06c835
/* Set program_name, based on argv[0], and original installation prefix and
Packit Bot 06c835
   directory, for relocatability.  */
Packit Bot 06c835
void
Packit Bot 06c835
set_program_name_and_installdir (const char *argv0,
Packit Bot 06c835
                                 const char *orig_installprefix,
Packit Bot 06c835
                                 const char *orig_installdir)
Packit Bot 06c835
{
Packit Bot 06c835
  const char *argv0_stripped = argv0;
Packit Bot 06c835
Packit Bot 06c835
  /* Relocatable programs are renamed to .bin by install-reloc.  Or, more
Packit Bot 06c835
     generally, their suffix is changed from $exeext to .bin$exeext.
Packit Bot 06c835
     Remove the ".bin" here.  */
Packit Bot 06c835
  {
Packit Bot 06c835
    size_t argv0_len = strlen (argv0);
Packit Bot 06c835
    const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
Packit Bot 06c835
    if (argv0_len > 4 + exeext_len)
Packit Bot 06c835
      if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
Packit Bot 06c835
        {
Packit Bot 06c835
          if (sizeof (EXEEXT) > sizeof (""))
Packit Bot 06c835
            {
Packit Bot 06c835
              /* Compare using an inlined copy of c_strncasecmp(), because
Packit Bot 06c835
                 the filenames may have undergone a case conversion since
Packit Bot 06c835
                 they were packaged.  In other words, EXEEXT may be ".exe"
Packit Bot 06c835
                 on one system and ".EXE" on another.  */
Packit Bot 06c835
              static const char exeext[] = EXEEXT;
Packit Bot 06c835
              const char *s1 = argv0 + argv0_len - exeext_len;
Packit Bot 06c835
              const char *s2 = exeext;
Packit Bot 06c835
              for (; *s1 != '\0'; s1++, s2++)
Packit Bot 06c835
                {
Packit Bot 06c835
                  unsigned char c1 = *s1;
Packit Bot 06c835
                  unsigned char c2 = *s2;
Packit Bot 06c835
                  if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
Packit Bot 06c835
                      != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
Packit Bot 06c835
                    goto done_stripping;
Packit Bot 06c835
                }
Packit Bot 06c835
            }
Packit Bot 06c835
          /* Remove ".bin" before EXEEXT or its equivalent.  */
Packit Bot 06c835
          {
Packit Bot 06c835
            char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
Packit Bot 06c835
#ifdef NO_XMALLOC
Packit Bot 06c835
            if (shorter != NULL)
Packit Bot 06c835
#endif
Packit Bot 06c835
              {
Packit Bot 06c835
                memcpy (shorter, argv0, argv0_len - exeext_len - 4);
Packit Bot 06c835
                if (sizeof (EXEEXT) > sizeof (""))
Packit Bot 06c835
                  memcpy (shorter + argv0_len - exeext_len - 4,
Packit Bot 06c835
                          argv0 + argv0_len - exeext_len - 4,
Packit Bot 06c835
                          exeext_len);
Packit Bot 06c835
                shorter[argv0_len - 4] = '\0';
Packit Bot 06c835
                argv0_stripped = shorter;
Packit Bot 06c835
              }
Packit Bot 06c835
          }
Packit Bot 06c835
         done_stripping: ;
Packit Bot 06c835
      }
Packit Bot 06c835
  }
Packit Bot 06c835
Packit Bot 06c835
  set_program_name (argv0_stripped);
Packit Bot 06c835
Packit Bot 06c835
  prepare_relocate (orig_installprefix, orig_installdir, argv0);
Packit Bot 06c835
}
Packit Bot 06c835
Packit Bot 06c835
/* Return the full pathname of the current executable, based on the earlier
Packit Bot 06c835
   call to set_program_name_and_installdir.  Return NULL if unknown.  */
Packit Bot 06c835
char *
Packit Bot 06c835
get_full_program_name (void)
Packit Bot 06c835
{
Packit Bot 06c835
  return executable_fullname;
Packit Bot 06c835
}
Packit Bot 06c835
Packit Bot 06c835
#endif