1 /*!
2  * \file src/lrealpath.c
3  *
4  * \brief Libiberty realpath.
5  *
6  * Like realpath, but more consistent behavior.
7  *
8  * Based on gdb_realpath from GDB.
9  *
10  * Copyright 2003 Free Software Foundation, Inc.
11  *
12  * This file is part of the libiberty library.
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27  */
28 
29 /*
30 
31 @deftypefn Replacement {const char*} lrealpath (const char *@var{name})
32 
33 Given a pointer to a string containing a pathname, returns a canonical
34 version of the filename.  Symlinks will be resolved, and ``.'' and ``..''
35 components will be simplified.  The returned value will be allocated using
36 @code{malloc}, or @code{NULL} will be returned on a memory allocation error.
37 
38 @end deftypefn
39 
40 */
41 
42 #include "config.h"
43 #include "lrealpath.h"
44 
45 #ifdef HAVE_LIMITS_H
46 #include <limits.h>
47 #endif
48 #ifdef HAVE_STDLIB_H
49 #include <stdlib.h>
50 #endif
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54 #ifdef HAVE_STRING_H
55 #include <string.h>
56 #endif
57 
58 /* On GNU libc systems the declaration is only visible with _GNU_SOURCE.  */
59 #if defined(HAVE_CANONICALIZE_FILE_NAME) \
60     && defined(NEED_DECLARATION_CANONICALIZE_FILE_NAME)
61 extern char *canonicalize_file_name (const char *);
62 #endif
63 
64 #if defined(HAVE_REALPATH)
65 # if defined (PATH_MAX)
66 #  define REALPATH_LIMIT PATH_MAX
67 # else
68 #  if defined (MAXPATHLEN)
69 #   define REALPATH_LIMIT MAXPATHLEN
70 #  endif
71 # endif
72 #else
73   /* cygwin has realpath, so it won't get here.  */
74 # if defined (_WIN32)
75 #  define WIN32_LEAN_AND_MEAN
76 #  include <windows.h> /* for GetFullPathName */
77 # endif
78 #endif
79 
80 /*!
81  * \brief A well-defined realpath () that is always compiled in.
82  */
83 char *
lrealpath(const char * filename)84 lrealpath (const char *filename)
85 {
86   /* Method 1: The system has a compile time upper bound on a filename
87      path.  Use that and realpath() to canonicalize the name.  This is
88      the most common case.  Note that, if there isn't a compile time
89      upper bound, you want to avoid realpath() at all costs.  */
90 #if defined(REALPATH_LIMIT)
91   {
92     char buf[REALPATH_LIMIT];
93     const char *rp = realpath (filename, buf);
94     if (rp == NULL)
95       rp = filename;
96     return strdup (rp);
97   }
98   /* REALPATH_LIMIT */
99 
100   /* Method 2: The host system (i.e., GNU) has the function
101      canonicalize_file_name() which malloc's a chunk of memory and
102      returns that, use that.  */
103 #elif defined(HAVE_CANONICALIZE_FILE_NAME)
104   {
105     char *rp = canonicalize_file_name (filename);
106     if (rp == NULL)
107       return strdup (filename);
108     else
109       return rp;
110   }
111   /* HAVE_CANONICALIZE_FILE_NAME */
112 
113   /* Method 3: Now we're getting desperate!  The system doesn't have a
114      compile time buffer size and no alternative function.  Query the
115      OS, using pathconf(), for the buffer limit.  Care is needed
116      though, some systems do not limit PATH_MAX (return -1 for
117      pathconf()) making it impossible to pass a correctly sized buffer
118      to realpath() (it could always overflow).  On those systems, we
119      skip this.  */
120 #elif defined (HAVE_REALPATH) && defined (HAVE_UNISTD_H)
121   {
122     /* Find out the max path size.  */
123     long path_max = pathconf ("/", _PC_PATH_MAX);
124     if (path_max > 0)
125       {
126 	/* PATH_MAX is bounded.  */
127 	char *buf, *rp, *ret;
128 	buf = (char *) malloc (path_max);
129 	if (buf == NULL)
130 	  return NULL;
131 	rp = realpath (filename, buf);
132 	ret = strdup (rp ? rp : filename);
133 	free (buf);
134 	return ret;
135       }
136   }
137   /* HAVE_REALPATH && HAVE_UNISTD_H */
138 
139   /* The MS Windows method.  If we don't have realpath, we assume we
140      don't have symlinks and just canonicalize to a Windows absolute
141      path.  GetFullPath converts ../ and ./ in relative paths to
142      absolute paths, filling in current drive if one is not given
143      or using the current directory of a specified drive (eg, "E:foo").
144      It also converts all forward slashes to back slashes.  */
145 #elif defined (_WIN32)
146   {
147     char buf[MAX_PATH];
148     char* basename;
149     DWORD len = GetFullPathName (filename, MAX_PATH, buf, &basename);
150     if (len == 0 || len > MAX_PATH - 1)
151       return strdup (filename);
152     else
153       {
154 	/* The file system is case-preserving but case-insensitive,
155 	   Canonicalize to lowercase, using the codepage associated
156 	   with the process locale.  */
157         CharLowerBuff (buf, len);
158         return strdup (buf);
159       }
160   }
161 #else
162 
163   /* This system is a lost cause, just duplicate the filename.  */
164   return strdup (filename);
165 #endif
166 }
167