1 /* realpath - print the resolved path
2 Copyright (C) 2011-2016 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Written by Pádraig Brady. */
22 #include <sys/types.h>
25 #include "canonicalize.h"
30 /* The official name of this program (e.g., no 'g' prefix). */
31 #define PROGRAM_NAME "realpath"
33 #define AUTHORS proper_name ("Padraig Brady")
37 RELATIVE_TO_OPTION
= CHAR_MAX
+ 1,
41 static bool verbose
= true;
44 static const char *can_relative_to
;
45 static const char *can_relative_base
;
47 static struct option
const longopts
[] =
49 {"canonicalize-existing", no_argument
, NULL
, 'e'},
50 {"canonicalize-missing", no_argument
, NULL
, 'm'},
51 {"relative-to", required_argument
, NULL
, RELATIVE_TO_OPTION
},
52 {"relative-base", required_argument
, NULL
, RELATIVE_BASE_OPTION
},
53 {"quiet", no_argument
, NULL
, 'q'},
54 {"strip", no_argument
, NULL
, 's'},
55 {"no-symlinks", no_argument
, NULL
, 's'},
56 {"zero", no_argument
, NULL
, 'z'},
57 {"logical", no_argument
, NULL
, 'L'},
58 {"physical", no_argument
, NULL
, 'P'},
59 {GETOPT_HELP_OPTION_DECL
},
60 {GETOPT_VERSION_OPTION_DECL
},
67 if (status
!= EXIT_SUCCESS
)
71 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
73 Print the resolved absolute file name;\n\
74 all but the last component must exist\n\
78 -e, --canonicalize-existing all components of the path must exist\n\
79 -m, --canonicalize-missing no path components need exist or be a directory\
81 -L, --logical resolve '..' components before symlinks\n\
82 -P, --physical resolve symlinks as encountered (default)\n\
83 -q, --quiet suppress most error messages\n\
84 --relative-to=FILE print the resolved path relative to FILE\n\
85 --relative-base=FILE print absolute paths unless paths below FILE\n\
86 -s, --strip, --no-symlinks don't expand symlinks\n\
87 -z, --zero end each output line with NUL, not newline\n\
90 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
91 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
92 emit_ancillary_info (PROGRAM_NAME
);
97 /* A wrapper around canonicalize_filename_mode(),
98 to call it twice when in LOGICAL mode. */
100 realpath_canon (const char *fname
, int can_mode
)
102 char *can_fname
= canonicalize_filename_mode (fname
, can_mode
);
103 if (logical
&& can_fname
) /* canonicalize again to resolve symlinks. */
105 can_mode
&= ~CAN_NOLINKS
;
106 char *can_fname2
= canonicalize_filename_mode (can_fname
, can_mode
);
113 /* Test whether canonical prefix is parent or match of path. */
114 static bool _GL_ATTRIBUTE_PURE
115 path_prefix (const char *prefix
, const char *path
)
117 /* We already know prefix[0] and path[0] are '/'. */
121 /* '/' is the prefix of everything except '//' (since we know '//'
122 is only present after canonicalization if it is distinct). */
126 /* Likewise, '//' is a prefix of any double-slash path. */
127 if (*prefix
== '/' && !prefix
[1])
130 /* Any other prefix has a non-slash portion. */
131 while (*prefix
&& *path
)
133 if (*prefix
!= *path
)
138 return (!*prefix
&& (*path
== '/' || !*path
));
142 isdir (const char *path
)
145 if (stat (path
, &sb
) != 0)
146 die (EXIT_FAILURE
, errno
, _("cannot stat %s"), quoteaf (path
));
147 return S_ISDIR (sb
.st_mode
);
151 process_path (const char *fname
, int can_mode
)
153 char *can_fname
= realpath_canon (fname
, can_mode
);
157 error (0, errno
, "%s", quotef (fname
));
162 || (can_relative_base
&& !path_prefix (can_relative_base
, can_fname
))
163 || (can_relative_to
&& !relpath (can_fname
, can_relative_to
, NULL
, 0)))
164 fputs (can_fname
, stdout
);
166 putchar (use_nuls
? '\0' : '\n');
174 main (int argc
, char **argv
)
177 int can_mode
= CAN_ALL_BUT_LAST
;
178 const char *relative_to
= NULL
;
179 const char *relative_base
= NULL
;
181 initialize_main (&argc
, &argv
);
182 set_program_name (argv
[0]);
183 setlocale (LC_ALL
, "");
184 bindtextdomain (PACKAGE
, LOCALEDIR
);
185 textdomain (PACKAGE
);
187 atexit (close_stdout
);
191 int c
= getopt_long (argc
, argv
, "eLmPqsz", longopts
, NULL
);
197 can_mode
&= ~CAN_MODE_MASK
;
198 can_mode
|= CAN_EXISTING
;
201 can_mode
&= ~CAN_MODE_MASK
;
202 can_mode
|= CAN_MISSING
;
205 can_mode
|= CAN_NOLINKS
;
209 can_mode
|= CAN_NOLINKS
;
213 can_mode
&= ~CAN_NOLINKS
;
222 case RELATIVE_TO_OPTION
:
223 relative_to
= optarg
;
225 case RELATIVE_BASE_OPTION
:
226 relative_base
= optarg
;
228 case_GETOPT_HELP_CHAR
;
229 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
231 usage (EXIT_FAILURE
);
237 error (0, 0, _("missing operand"));
238 usage (EXIT_FAILURE
);
241 if (relative_base
&& !relative_to
)
242 relative_to
= relative_base
;
244 bool need_dir
= (can_mode
& CAN_MODE_MASK
) == CAN_EXISTING
;
247 can_relative_to
= realpath_canon (relative_to
, can_mode
);
248 if (!can_relative_to
)
249 die (EXIT_FAILURE
, errno
, "%s", quotef (relative_to
));
250 if (need_dir
&& !isdir (can_relative_to
))
251 die (EXIT_FAILURE
, ENOTDIR
, "%s", quotef (relative_to
));
253 if (relative_base
== relative_to
)
254 can_relative_base
= can_relative_to
;
255 else if (relative_base
)
257 char *base
= realpath_canon (relative_base
, can_mode
);
259 die (EXIT_FAILURE
, errno
, "%s", quotef (relative_base
));
260 if (need_dir
&& !isdir (base
))
261 die (EXIT_FAILURE
, ENOTDIR
, "%s", quotef (relative_base
));
262 /* --relative-to is a no-op if it does not have --relative-base
264 if (path_prefix (base
, can_relative_to
))
265 can_relative_base
= base
;
269 can_relative_base
= can_relative_to
;
270 can_relative_to
= NULL
;
274 for (; optind
< argc
; ++optind
)
275 ok
&= process_path (argv
[optind
], can_mode
);
277 return ok
? EXIT_SUCCESS
: EXIT_FAILURE
;