make-relative-prefix.c [plain text]
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include "ansidecl.h"
#include "libiberty.h"
#ifndef R_OK
#define R_OK 4
#define W_OK 2
#define X_OK 1
#endif
#ifndef DIR_SEPARATOR
# define DIR_SEPARATOR '/'
#endif
#if defined (_WIN32) || defined (__MSDOS__) \
|| defined (__DJGPP__) || defined (__OS2__)
# define HAVE_DOS_BASED_FILE_SYSTEM
# define HAVE_HOST_EXECUTABLE_SUFFIX
# define HOST_EXECUTABLE_SUFFIX ".exe"
# ifndef DIR_SEPARATOR_2
# define DIR_SEPARATOR_2 '\\'
# endif
# define PATH_SEPARATOR ';'
#else
# define PATH_SEPARATOR ':'
#endif
#ifndef DIR_SEPARATOR_2
# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
#else
# define IS_DIR_SEPARATOR(ch) \
(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
#endif
#define DIR_UP ".."
static char *save_string (const char *, int);
static char **split_directories (const char *, int *);
static void free_split_directories (char **);
static char *
save_string (const char *s, int len)
{
char *result = (char *) malloc (len + 1);
memcpy (result, s, len);
result[len] = 0;
return result;
}
static char **
split_directories (const char *name, int *ptr_num_dirs)
{
int num_dirs = 0;
char **dirs;
const char *p, *q;
int ch;
p = name;
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
{
p += 3;
num_dirs++;
}
#endif
while ((ch = *p++) != '\0')
{
if (IS_DIR_SEPARATOR (ch))
{
num_dirs++;
while (IS_DIR_SEPARATOR (*p))
p++;
}
}
dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
if (dirs == NULL)
return NULL;
num_dirs = 0;
p = name;
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
{
dirs[num_dirs++] = save_string (p, 3);
if (dirs[num_dirs - 1] == NULL)
{
free (dirs);
return NULL;
}
p += 3;
}
#endif
q = p;
while ((ch = *p++) != '\0')
{
if (IS_DIR_SEPARATOR (ch))
{
while (IS_DIR_SEPARATOR (*p))
p++;
dirs[num_dirs++] = save_string (q, p - q);
if (dirs[num_dirs - 1] == NULL)
{
dirs[num_dirs] = NULL;
free_split_directories (dirs);
return NULL;
}
q = p;
}
}
if (p - 1 - q > 0)
dirs[num_dirs++] = save_string (q, p - 1 - q);
dirs[num_dirs] = NULL;
if (dirs[num_dirs - 1] == NULL)
{
free_split_directories (dirs);
return NULL;
}
if (ptr_num_dirs)
*ptr_num_dirs = num_dirs;
return dirs;
}
static void
free_split_directories (char **dirs)
{
int i = 0;
while (dirs[i] != NULL)
free (dirs[i++]);
free ((char *) dirs);
}
char *
make_relative_prefix (const char *progname,
const char *bin_prefix, const char *prefix)
{
char **prog_dirs, **bin_dirs, **prefix_dirs;
int prog_num, bin_num, prefix_num;
int i, n, common;
int needed_len;
char *ret, *ptr, *full_progname = NULL;
if (progname == NULL || bin_prefix == NULL || prefix == NULL)
return NULL;
if (lbasename (progname) == progname)
{
char *temp;
temp = getenv ("PATH");
if (temp)
{
char *startp, *endp, *nstore;
size_t prefixlen = strlen (temp) + 1;
if (prefixlen < 2)
prefixlen = 2;
nstore = (char *) alloca (prefixlen + strlen (progname) + 1);
startp = endp = temp;
while (1)
{
if (*endp == PATH_SEPARATOR || *endp == 0)
{
if (endp == startp)
{
nstore[0] = '.';
nstore[1] = DIR_SEPARATOR;
nstore[2] = '\0';
}
else
{
strncpy (nstore, startp, endp - startp);
if (! IS_DIR_SEPARATOR (endp[-1]))
{
nstore[endp - startp] = DIR_SEPARATOR;
nstore[endp - startp + 1] = 0;
}
else
nstore[endp - startp] = 0;
}
strcat (nstore, progname);
if (! access (nstore, X_OK)
#ifdef HAVE_HOST_EXECUTABLE_SUFFIX
|| ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
#endif
)
{
progname = nstore;
break;
}
if (*endp == 0)
break;
endp = startp = endp + 1;
}
else
endp++;
}
}
}
full_progname = lrealpath (progname);
if (full_progname == NULL)
return NULL;
prog_dirs = split_directories (full_progname, &prog_num);
bin_dirs = split_directories (bin_prefix, &bin_num);
free (full_progname);
if (bin_dirs == NULL || prog_dirs == NULL)
return NULL;
prog_num--;
if (prog_num == bin_num)
{
for (i = 0; i < bin_num; i++)
{
if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
break;
}
if (prog_num <= 0 || i == bin_num)
{
free_split_directories (prog_dirs);
free_split_directories (bin_dirs);
prog_dirs = bin_dirs = (char **) 0;
return NULL;
}
}
prefix_dirs = split_directories (prefix, &prefix_num);
if (prefix_dirs == NULL)
{
free_split_directories (prog_dirs);
free_split_directories (bin_dirs);
return NULL;
}
n = (prefix_num < bin_num) ? prefix_num : bin_num;
for (common = 0; common < n; common++)
{
if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
break;
}
if (common == 0)
{
free_split_directories (prog_dirs);
free_split_directories (bin_dirs);
free_split_directories (prefix_dirs);
return NULL;
}
needed_len = 0;
for (i = 0; i < prog_num; i++)
needed_len += strlen (prog_dirs[i]);
needed_len += sizeof (DIR_UP) * (bin_num - common);
for (i = common; i < prefix_num; i++)
needed_len += strlen (prefix_dirs[i]);
needed_len += 1;
ret = (char *) malloc (needed_len);
if (ret == NULL)
return NULL;
*ret = '\0';
for (i = 0; i < prog_num; i++)
strcat (ret, prog_dirs[i]);
ptr = ret + strlen(ret);
for (i = common; i < bin_num; i++)
{
strcpy (ptr, DIR_UP);
ptr += sizeof (DIR_UP) - 1;
*(ptr++) = DIR_SEPARATOR;
}
*ptr = '\0';
for (i = common; i < prefix_num; i++)
strcat (ret, prefix_dirs[i]);
free_split_directories (prog_dirs);
free_split_directories (bin_dirs);
free_split_directories (prefix_dirs);
return ret;
}