(linenum→info "unix/slp.c:2238")

binutils/2.18/libiberty/make-relative-prefix.c

    1: /* Relative (relocatable) prefix support.
    2:    Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
    3:    1999, 2000, 2001, 2002, 2006 Free Software Foundation, Inc.
    4: 
    5: This file is part of libiberty.
    6: 
    7: GCC is free software; you can redistribute it and/or modify it under
    8: the terms of the GNU General Public License as published by the Free
    9: Software Foundation; either version 2, or (at your option) any later
   10: version.
   11: 
   12: GCC is distributed in the hope that it will be useful, but WITHOUT ANY
   13: WARRANTY; without even the implied warranty of MERCHANTABILITY or
   14: FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   15: for more details.
   16: 
   17: You should have received a copy of the GNU General Public License
   18: along with GCC; see the file COPYING.  If not, write to the Free
   19: Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
   20: 02110-1301, USA.  */
   21: 
   22: /*
   23: 
   24: @deftypefn Extension {const char*} make_relative_prefix (const char *@var{progname}, const char *@var{bin_prefix}, const char *@var{prefix})
   25: 
   26: Given three paths @var{progname}, @var{bin_prefix}, @var{prefix},
   27: return the path that is in the same position relative to
   28: @var{progname}'s directory as @var{prefix} is relative to
   29: @var{bin_prefix}.  That is, a string starting with the directory
   30: portion of @var{progname}, followed by a relative pathname of the
   31: difference between @var{bin_prefix} and @var{prefix}.
   32: 
   33: If @var{progname} does not contain any directory separators,
   34: @code{make_relative_prefix} will search @env{PATH} to find a program
   35: named @var{progname}.  Also, if @var{progname} is a symbolic link,
   36: the symbolic link will be resolved.
   37: 
   38: For example, if @var{bin_prefix} is @code{/alpha/beta/gamma/gcc/delta},
   39: @var{prefix} is @code{/alpha/beta/gamma/omega/}, and @var{progname} is
   40: @code{/red/green/blue/gcc}, then this function will return
   41: @code{/red/green/blue/../../omega/}.
   42: 
   43: The return value is normally allocated via @code{malloc}.  If no
   44: relative prefix can be found, return @code{NULL}.
   45: 
   46: @end deftypefn
   47: 
   48: */
   49: 
   50: #ifdef HAVE_CONFIG_H
   51: #include "config.h"
   52: #endif
   53: 
   54: #ifdef HAVE_STDLIB_H
   55: #include <stdlib.h>
   56: #endif
   57: #ifdef HAVE_UNISTD_H
   58: #include <unistd.h>
   59: #endif
   60: 
   61: #include <string.h>
   62: 
   63: #include "ansidecl.h"
   64: #include "libiberty.h"
   65: 
   66: #ifndef R_OK
   67: #define R_OK 4
   68: #define W_OK 2
   69: #define X_OK 1
   70: #endif
   71: 
   72: #ifndef DIR_SEPARATOR
   73: #  define DIR_SEPARATOR '/'
   74: #endif
   75: 
   76: #if defined (_WIN32) || defined (__MSDOS__) \
   77:     || defined (__DJGPP__) || defined (__OS2__)
   78: #  define HAVE_DOS_BASED_FILE_SYSTEM
   79: #  define HAVE_HOST_EXECUTABLE_SUFFIX
   80: #  define HOST_EXECUTABLE_SUFFIX ".exe"
   81: #  ifndef DIR_SEPARATOR_2 
   82: #    define DIR_SEPARATOR_2 '\\'
   83: #  endif
   84: #  define PATH_SEPARATOR ';'
   85: #else
   86: #  define PATH_SEPARATOR ':'
   87: #endif
   88: 
   89: #ifndef DIR_SEPARATOR_2
   90: #  define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
   91: #else
   92: #  define IS_DIR_SEPARATOR(ch) \
   93:         (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
   94: #endif
   95: 
   96: #define DIR_UP ".."
   97: 
   98: static char *save_string (const char *, int);
   99: static char **split_directories (const char *, int *);
  100: static void free_split_directories (char **);
  101: 
  102: static char *
  103: save_string (const char *s, int len)
  104: {
  105:   char *result = (char *) malloc (len + 1);
  106: 
  107:   memcpy (result, s, len);
  108:   result[len] = 0;
  109:   return result;
  110: }
  111: 
  112: /* Split a filename into component directories.  */
  113: 
  114: static char **
  115: split_directories (const char *name, int *ptr_num_dirs)
  116: {
  117:   int num_dirs = 0;
  118:   char **dirs;
  119:   const char *p, *q;
  120:   int ch;
  121: 
  122:   /* Count the number of directories.  Special case MSDOS disk names as part
  123:      of the initial directory.  */
  124:   p = name;
  125: #ifdef HAVE_DOS_BASED_FILE_SYSTEM
  126:   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
  127:     {
  128:       p += 3;
  129:       num_dirs++;
  130:     }
  131: #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
  132: 
  133:   while ((ch = *p++) != '\0')
  134:     {
  135:       if (IS_DIR_SEPARATOR (ch))
  136:         {
  137:           num_dirs++;
  138:           while (IS_DIR_SEPARATOR (*p))
  139:             p++;
  140:         }
  141:     }
  142: 
  143:   dirs = (char **) malloc (sizeof (char *) * (num_dirs + 2));
  144:   if (dirs == NULL)
  145:     return NULL;
  146: 
  147:   /* Now copy the directory parts.  */
  148:   num_dirs = 0;
  149:   p = name;
  150: #ifdef HAVE_DOS_BASED_FILE_SYSTEM
  151:   if (name[1] == ':' && IS_DIR_SEPARATOR (name[2]))
  152:     {
  153:       dirs[num_dirs++] = save_string (p, 3);
  154:       if (dirs[num_dirs - 1] == NULL)
  155:         {
  156:           free (dirs);
  157:           return NULL;
  158:         }
  159:       p += 3;
  160:     }
  161: #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
  162: 
  163:   q = p;
  164:   while ((ch = *p++) != '\0')
  165:     {
  166:       if (IS_DIR_SEPARATOR (ch))
  167:         {
  168:           while (IS_DIR_SEPARATOR (*p))
  169:             p++;
  170: 
  171:           dirs[num_dirs++] = save_string (q, p - q);
  172:           if (dirs[num_dirs - 1] == NULL)
  173:             {
  174:               dirs[num_dirs] = NULL;
  175:               free_split_directories (dirs);
  176:               return NULL;
  177:             }
  178:           q = p;
  179:         }
  180:     }
  181: 
  182:   if (p - 1 - q > 0)
  183:     dirs[num_dirs++] = save_string (q, p - 1 - q);
  184:   dirs[num_dirs] = NULL;
  185: 
  186:   if (dirs[num_dirs - 1] == NULL)
  187:     {
  188:       free_split_directories (dirs);
  189:       return NULL;
  190:     }
  191: 
  192:   if (ptr_num_dirs)
  193:     *ptr_num_dirs = num_dirs;
  194:   return dirs;
  195: }
  196: 
  197: /* Release storage held by split directories.  */
  198: 
  199: static void
  200: free_split_directories (char **dirs)
  201: {
  202:   int i = 0;
  203: 
  204:   while (dirs[i] != NULL)
  205:     free (dirs[i++]);
  206: 
  207:   free ((char *) dirs);
  208: }
  209: 
  210: /* Given three strings PROGNAME, BIN_PREFIX, PREFIX, return a string that gets
  211:    to PREFIX starting with the directory portion of PROGNAME and a relative
  212:    pathname of the difference between BIN_PREFIX and PREFIX.
  213: 
  214:    For example, if BIN_PREFIX is /alpha/beta/gamma/gcc/delta, PREFIX is
  215:    /alpha/beta/gamma/omega/, and PROGNAME is /red/green/blue/gcc, then this
  216:    function will return /red/green/blue/../../omega/.
  217: 
  218:    If no relative prefix can be found, return NULL.  */
  219: 
  220: static char *
  221: make_relative_prefix_1 (const char *progname, const char *bin_prefix,
  222:                         const char *prefix, const int resolve_links)
  223: {
  224:   char **prog_dirs, **bin_dirs, **prefix_dirs;
  225:   int prog_num, bin_num, prefix_num;
  226:   int i, n, common;
  227:   int needed_len;
  228:   char *ret, *ptr, *full_progname = NULL;
  229: 
  230:   if (progname == NULL || bin_prefix == NULL || prefix == NULL)
  231:     return NULL;
  232: 
  233:   /* If there is no full pathname, try to find the program by checking in each
  234:      of the directories specified in the PATH environment variable.  */
  235:   if (lbasename (progname) == progname)
  236:     {
  237:       char *temp;
  238: 
  239:       temp = getenv ("PATH");
  240:       if (temp)
  241:         {
  242:           char *startp, *endp, *nstore;
  243:           size_t prefixlen = strlen (temp) + 1;
  244:           if (prefixlen < 2)
  245:             prefixlen = 2;
  246: 
  247:           nstore = (char *) alloca (prefixlen + strlen (progname) + 1);
  248: 
  249:           startp = endp = temp;
  250:           while (1)
  251:             {
  252:               if (*endp == PATH_SEPARATOR || *endp == 0)
  253:                 {
  254:                   if (endp == startp)
  255:                     {
  256:                       nstore[0] = '.';
  257:                       nstore[1] = DIR_SEPARATOR;
  258:                       nstore[2] = '\0';
  259:                     }
  260:                   else
  261:                     {
  262:                       strncpy (nstore, startp, endp - startp);
  263:                       if (! IS_DIR_SEPARATOR (endp[-1]))
  264:                         {
  265:                           nstore[endp - startp] = DIR_SEPARATOR;
  266:                           nstore[endp - startp + 1] = 0;
  267:                         }
  268:                       else
  269:                         nstore[endp - startp] = 0;
  270:                     }
  271:                   strcat (nstore, progname);
  272:                   if (! access (nstore, X_OK)
  273: #ifdef HAVE_HOST_EXECUTABLE_SUFFIX
  274:                       || ! access (strcat (nstore, HOST_EXECUTABLE_SUFFIX), X_OK)
  275: #endif
  276:                       )
  277:                     {
  278:                       progname = nstore;
  279:                       break;
  280:                     }
  281: 
  282:                   if (*endp == 0)
  283:                     break;
  284:                   endp = startp = endp + 1;
  285:                 }
  286:               else
  287:                 endp++;
  288:             }
  289:         }
  290:     }
  291: 
  292:   if ( resolve_links )
  293:     {
  294:       full_progname = lrealpath (progname);
  295:       if (full_progname == NULL)
  296:         return NULL;
  297:     }
  298:   else
  299:     full_progname = strdup(progname);
  300: 
  301:   prog_dirs = split_directories (full_progname, &prog_num);
  302:   free (full_progname);
  303:   if (prog_dirs == NULL)
  304:     return NULL;
  305: 
  306:   bin_dirs = split_directories (bin_prefix, &bin_num);
  307:   if (bin_dirs == NULL)
  308:     {
  309:       free_split_directories (prog_dirs);
  310:       return NULL;
  311:     }
  312: 
  313:   /* Remove the program name from comparison of directory names.  */
  314:   prog_num--;
  315: 
  316:   /* If we are still installed in the standard location, we don't need to
  317:      specify relative directories.  Also, if argv[0] still doesn't contain
  318:      any directory specifiers after the search above, then there is not much
  319:      we can do.  */
  320:   if (prog_num == bin_num)
  321:     {
  322:       for (i = 0; i < bin_num; i++)
  323:         {
  324:           if (strcmp (prog_dirs[i], bin_dirs[i]) != 0)
  325:             break;
  326:         }
  327: 
  328:       if (prog_num <= 0 || i == bin_num)
  329:         {
  330:           free_split_directories (prog_dirs);
  331:           free_split_directories (bin_dirs);
  332:           prog_dirs = bin_dirs = (char **) 0;
  333:           return NULL;
  334:         }
  335:     }
  336: 
  337:   prefix_dirs = split_directories (prefix, &prefix_num);
  338:   if (prefix_dirs == NULL)
  339:     {
  340:       free_split_directories (prog_dirs);
  341:       free_split_directories (bin_dirs);
  342:       return NULL;
  343:     }
  344: 
  345:   /* Find how many directories are in common between bin_prefix & prefix.  */
  346:   n = (prefix_num < bin_num) ? prefix_num : bin_num;
  347:   for (common = 0; common < n; common++)
  348:     {
  349:       if (strcmp (bin_dirs[common], prefix_dirs[common]) != 0)
  350:         break;
  351:     }
  352: 
  353:   /* If there are no common directories, there can be no relative prefix.  */
  354:   if (common == 0)
  355:     {
  356:       free_split_directories (prog_dirs);
  357:       free_split_directories (bin_dirs);
  358:       free_split_directories (prefix_dirs);
  359:       return NULL;
  360:     }
  361: 
  362:   /* Two passes: first figure out the size of the result string, and
  363:      then construct it.  */
  364:   needed_len = 0;
  365:   for (i = 0; i < prog_num; i++)
  366:     needed_len += strlen (prog_dirs[i]);
  367:   needed_len += sizeof (DIR_UP) * (bin_num - common);
  368:   for (i = common; i < prefix_num; i++)
  369:     needed_len += strlen (prefix_dirs[i]);
  370:   needed_len += 1; /* Trailing NUL.  */
  371: 
  372:   ret = (char *) malloc (needed_len);
  373:   if (ret == NULL)
  374:     return NULL;
  375: 
  376:   /* Build up the pathnames in argv[0].  */
  377:   *ret = '\0';
  378:   for (i = 0; i < prog_num; i++)
  379:     strcat (ret, prog_dirs[i]);
  380: 
  381:   /* Now build up the ..'s.  */
  382:   ptr = ret + strlen(ret);
  383:   for (i = common; i < bin_num; i++)
  384:     {
  385:       strcpy (ptr, DIR_UP);
  386:       ptr += sizeof (DIR_UP) - 1;
  387:       *(ptr++) = DIR_SEPARATOR;
  388:     }
  389:   *ptr = '\0';
  390: 
  391:   /* Put in directories to move over to prefix.  */
  392:   for (i = common; i < prefix_num; i++)
  393:     strcat (ret, prefix_dirs[i]);
  394: 
  395:   free_split_directories (prog_dirs);
  396:   free_split_directories (bin_dirs);
  397:   free_split_directories (prefix_dirs);
  398: 
  399:   return ret;
  400: }
  401: 
  402: 
  403: /* Do the full job, including symlink resolution.
  404:    This path will find files installed in the same place as the
  405:    program even when a soft link has been made to the program
  406:    from somwhere else. */
  407: 
  408: char *
  409: make_relative_prefix (const char *progname, const char *bin_prefix,
  410:                       const char *prefix)
  411: {
  412:   return make_relative_prefix_1 (progname, bin_prefix, prefix, 1);
  413: }
  414: 
  415: /* Make the relative pathname without attempting to resolve any links.
  416:    '..' etc may also be left in the pathname.
  417:    This will find the files the user meant the program to find if the
  418:    installation is patched together with soft links. */
  419: 
  420: char *
  421: make_relative_prefix_ignore_links (const char *progname,
  422:                                    const char *bin_prefix,
  423:                                    const char *prefix)
  424: {
  425:   return make_relative_prefix_1 (progname, bin_prefix, prefix, 0);
  426: }
  427: 
Syntax (Markdown)