@@ -15,16 +15,34 @@ int is_directory(const char *path)
1515#define MAXDEPTH 5
1616
1717/*
18- * Use this to get the real path, i.e. resolve links. If you want an
19- * absolute path but don't mind links, use absolute_path.
18+ * Return the real path (i.e., absolute path, with symlinks resolved
19+ * and extra slashes removed) equivalent to the specified path. (If
20+ * you want an absolute path but don't mind links, use
21+ * absolute_path().) The return value is a pointer to a static
22+ * buffer.
23+ *
24+ * The input and all intermediate paths must be shorter than MAX_PATH.
25+ * The directory part of path (i.e., everything up to the last
26+ * dir_sep) must denote a valid, existing directory, but the last
27+ * component need not exist. If die_on_error is set, then die with an
28+ * informative error message if there is a problem. Otherwise, return
29+ * NULL on errors (without generating any output).
2030 *
2131 * If path is our buffer, then return path, as it's already what the
2232 * user wants.
2333 */
24- const char * real_path (const char * path )
34+ static const char * real_path_internal (const char * path , int die_on_error )
2535{
2636 static char bufs [2 ][PATH_MAX + 1 ], * buf = bufs [0 ], * next_buf = bufs [1 ];
37+ char * retval = NULL ;
38+
39+ /*
40+ * If we have to temporarily chdir(), store the original CWD
41+ * here so that we can chdir() back to it at the end of the
42+ * function:
43+ */
2744 char cwd [1024 ] = "" ;
45+
2846 int buf_index = 1 ;
2947
3048 int depth = MAXDEPTH ;
@@ -35,11 +53,19 @@ const char *real_path(const char *path)
3553 if (path == buf || path == next_buf )
3654 return path ;
3755
38- if (!* path )
39- die ("The empty string is not a valid path" );
56+ if (!* path ) {
57+ if (die_on_error )
58+ die ("The empty string is not a valid path" );
59+ else
60+ goto error_out ;
61+ }
4062
41- if (strlcpy (buf , path , PATH_MAX ) >= PATH_MAX )
42- die ("Too long path: %.*s" , 60 , path );
63+ if (strlcpy (buf , path , PATH_MAX ) >= PATH_MAX ) {
64+ if (die_on_error )
65+ die ("Too long path: %.*s" , 60 , path );
66+ else
67+ goto error_out ;
68+ }
4369
4470 while (depth -- ) {
4571 if (!is_directory (buf )) {
@@ -54,20 +80,36 @@ const char *real_path(const char *path)
5480 }
5581
5682 if (* buf ) {
57- if (!* cwd && !getcwd (cwd , sizeof (cwd )))
58- die_errno ("Could not get current working directory" );
83+ if (!* cwd && !getcwd (cwd , sizeof (cwd ))) {
84+ if (die_on_error )
85+ die_errno ("Could not get current working directory" );
86+ else
87+ goto error_out ;
88+ }
5989
60- if (chdir (buf ))
61- die_errno ("Could not switch to '%s'" , buf );
90+ if (chdir (buf )) {
91+ if (die_on_error )
92+ die_errno ("Could not switch to '%s'" , buf );
93+ else
94+ goto error_out ;
95+ }
96+ }
97+ if (!getcwd (buf , PATH_MAX )) {
98+ if (die_on_error )
99+ die_errno ("Could not get current working directory" );
100+ else
101+ goto error_out ;
62102 }
63- if (!getcwd (buf , PATH_MAX ))
64- die_errno ("Could not get current working directory" );
65103
66104 if (last_elem ) {
67105 size_t len = strlen (buf );
68- if (len + strlen (last_elem ) + 2 > PATH_MAX )
69- die ("Too long path name: '%s/%s'" ,
70- buf , last_elem );
106+ if (len + strlen (last_elem ) + 2 > PATH_MAX ) {
107+ if (die_on_error )
108+ die ("Too long path name: '%s/%s'" ,
109+ buf , last_elem );
110+ else
111+ goto error_out ;
112+ }
71113 if (len && !is_dir_sep (buf [len - 1 ]))
72114 buf [len ++ ] = '/' ;
73115 strcpy (buf + len , last_elem );
@@ -77,10 +119,18 @@ const char *real_path(const char *path)
77119
78120 if (!lstat (buf , & st ) && S_ISLNK (st .st_mode )) {
79121 ssize_t len = readlink (buf , next_buf , PATH_MAX );
80- if (len < 0 )
81- die_errno ("Invalid symlink '%s'" , buf );
82- if (PATH_MAX <= len )
83- die ("symbolic link too long: %s" , buf );
122+ if (len < 0 ) {
123+ if (die_on_error )
124+ die_errno ("Invalid symlink '%s'" , buf );
125+ else
126+ goto error_out ;
127+ }
128+ if (PATH_MAX <= len ) {
129+ if (die_on_error )
130+ die ("symbolic link too long: %s" , buf );
131+ else
132+ goto error_out ;
133+ }
84134 next_buf [len ] = '\0' ;
85135 buf = next_buf ;
86136 buf_index = 1 - buf_index ;
@@ -89,10 +139,23 @@ const char *real_path(const char *path)
89139 break ;
90140 }
91141
142+ retval = buf ;
143+ error_out :
144+ free (last_elem );
92145 if (* cwd && chdir (cwd ))
93146 die_errno ("Could not change back to '%s'" , cwd );
94147
95- return buf ;
148+ return retval ;
149+ }
150+
151+ const char * real_path (const char * path )
152+ {
153+ return real_path_internal (path , 1 );
154+ }
155+
156+ const char * real_path_if_valid (const char * path )
157+ {
158+ return real_path_internal (path , 0 );
96159}
97160
98161static const char * get_pwd_cwd (void )
0 commit comments