2011-12-27 00:30:23 +00:00
|
|
|
/* realpath - print the resolved path
|
2023-01-01 14:50:15 +00:00
|
|
|
Copyright (C) 2011-2023 Free Software Foundation, Inc.
|
2011-12-27 00:30:23 +00:00
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2017-09-19 01:13:23 -07:00
|
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
2011-12-27 00:30:23 +00:00
|
|
|
|
|
|
|
|
/* Written by Pádraig Brady. */
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
|
|
#include "system.h"
|
|
|
|
|
#include "canonicalize.h"
|
2016-10-15 23:10:35 +01:00
|
|
|
#include "die.h"
|
2011-12-27 00:30:23 +00:00
|
|
|
#include "error.h"
|
2012-03-22 20:34:57 +00:00
|
|
|
#include "relpath.h"
|
2011-12-27 00:30:23 +00:00
|
|
|
|
2012-01-08 15:08:30 +01:00
|
|
|
/* The official name of this program (e.g., no 'g' prefix). */
|
2011-12-27 00:30:23 +00:00
|
|
|
#define PROGRAM_NAME "realpath"
|
|
|
|
|
|
2016-11-13 14:08:48 +00:00
|
|
|
#define AUTHORS proper_name ("Padraig Brady")
|
2011-12-27 00:30:23 +00:00
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
RELATIVE_TO_OPTION = CHAR_MAX + 1,
|
|
|
|
|
RELATIVE_BASE_OPTION
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool verbose = true;
|
|
|
|
|
static bool logical;
|
|
|
|
|
static bool use_nuls;
|
2021-04-11 18:23:21 +01:00
|
|
|
static char const *can_relative_to;
|
|
|
|
|
static char const *can_relative_base;
|
2011-12-27 00:30:23 +00:00
|
|
|
|
|
|
|
|
static struct option const longopts[] =
|
|
|
|
|
{
|
|
|
|
|
{"canonicalize-existing", no_argument, NULL, 'e'},
|
|
|
|
|
{"canonicalize-missing", no_argument, NULL, 'm'},
|
|
|
|
|
{"relative-to", required_argument, NULL, RELATIVE_TO_OPTION},
|
|
|
|
|
{"relative-base", required_argument, NULL, RELATIVE_BASE_OPTION},
|
|
|
|
|
{"quiet", no_argument, NULL, 'q'},
|
2013-11-29 12:18:06 +01:00
|
|
|
{"strip", no_argument, NULL, 's'},
|
2011-12-27 00:30:23 +00:00
|
|
|
{"no-symlinks", no_argument, NULL, 's'},
|
|
|
|
|
{"zero", no_argument, NULL, 'z'},
|
|
|
|
|
{"logical", no_argument, NULL, 'L'},
|
|
|
|
|
{"physical", no_argument, NULL, 'P'},
|
|
|
|
|
{GETOPT_HELP_OPTION_DECL},
|
|
|
|
|
{GETOPT_VERSION_OPTION_DECL},
|
|
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
usage (int status)
|
|
|
|
|
{
|
|
|
|
|
if (status != EXIT_SUCCESS)
|
2012-01-07 16:54:26 +01:00
|
|
|
emit_try_help ();
|
2011-12-27 00:30:23 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
|
|
|
|
|
fputs (_("\
|
|
|
|
|
Print the resolved absolute file name;\n\
|
|
|
|
|
all but the last component must exist\n\
|
|
|
|
|
\n\
|
|
|
|
|
"), stdout);
|
|
|
|
|
fputs (_("\
|
|
|
|
|
-e, --canonicalize-existing all components of the path must exist\n\
|
2015-07-15 14:32:31 +01:00
|
|
|
-m, --canonicalize-missing no path components need exist or be a directory\
|
|
|
|
|
\n\
|
2012-01-08 14:08:03 +01:00
|
|
|
-L, --logical resolve '..' components before symlinks\n\
|
2011-12-27 00:30:23 +00:00
|
|
|
-P, --physical resolve symlinks as encountered (default)\n\
|
|
|
|
|
-q, --quiet suppress most error messages\n\
|
2017-06-24 19:47:26 -04:00
|
|
|
--relative-to=DIR print the resolved path relative to DIR\n\
|
|
|
|
|
--relative-base=DIR print absolute paths unless paths below DIR\n\
|
2011-12-27 00:30:23 +00:00
|
|
|
-s, --strip, --no-symlinks don't expand symlinks\n\
|
2014-05-23 00:45:57 +01:00
|
|
|
-z, --zero end each output line with NUL, not newline\n\
|
2011-12-27 00:30:23 +00:00
|
|
|
"), stdout);
|
|
|
|
|
fputs (HELP_OPTION_DESCRIPTION, stdout);
|
|
|
|
|
fputs (VERSION_OPTION_DESCRIPTION, stdout);
|
2014-09-18 14:50:47 +01:00
|
|
|
emit_ancillary_info (PROGRAM_NAME);
|
2011-12-27 00:30:23 +00:00
|
|
|
}
|
|
|
|
|
exit (status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A wrapper around canonicalize_filename_mode(),
|
|
|
|
|
to call it twice when in LOGICAL mode. */
|
|
|
|
|
static char *
|
2021-04-11 18:23:21 +01:00
|
|
|
realpath_canon (char const *fname, int can_mode)
|
2011-12-27 00:30:23 +00:00
|
|
|
{
|
|
|
|
|
char *can_fname = canonicalize_filename_mode (fname, can_mode);
|
|
|
|
|
if (logical && can_fname) /* canonicalize again to resolve symlinks. */
|
|
|
|
|
{
|
|
|
|
|
can_mode &= ~CAN_NOLINKS;
|
|
|
|
|
char *can_fname2 = canonicalize_filename_mode (can_fname, can_mode);
|
|
|
|
|
free (can_fname);
|
|
|
|
|
return can_fname2;
|
|
|
|
|
}
|
|
|
|
|
return can_fname;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-04 11:50:53 -07:00
|
|
|
/* Test whether canonical prefix is parent or match of path. */
|
maint: prefer attribute.h in .c files
This will help us make the transition to C2x, where some
attributes must come at the start of function decls.
Leave the attributes alone in .h files for now,
as the Gnulib tradition is to not expose attribute.h to users.
* bootstrap.conf (gnulib_modules): Add ‘attribute’.
* gl/lib/randperm.c, src/make-prime-list.c, src/system.h:
Include attribute.h.
* gl/lib/strnumcmp.c (strnumcmp): Remove _GL_ATTRIBUTE_PURE here,
as this belongs in the .h file.
* gl/lib/strnumcmp.h (strnumcmp): Add _GL_ATTRIBUTE_PURE here.
* src/sort.c (human_numcompare, numcompare): Now ATTRIBUTE_PURE;
discovered due to strnumcmp.h change.
* gl/lib/randperm.c, src/copy.c, src/dd.c, src/df.c, src/digest.c:
* src/env.c, src/expr.c, src/factor.c, src/ls.c:
* src/make-prime-list.c, src/numfmt.c, src/od.c, src/pathchk.c:
* src/pinky.c, src/pr.c, src/ptx.c, src/realpath.c, src/relpath.c:
* src/seq.c, src/sort.c, src/stat.c, src/stty.c, src/system.h:
* src/tr.c, src/uniq.c, src/wc.c:
In .c files, crefer ATTRIBUTE_CONST to _GL_ATTRIBUTE_CONST, and
similarly for ATTRIBUTE_FORMAT and ATTRIBUTE_PURE.
* src/system.h (FALLTHROUGH): Remove; attribute.h defines it.
2021-10-31 22:30:38 -07:00
|
|
|
ATTRIBUTE_PURE
|
|
|
|
|
static bool
|
2021-04-11 18:23:21 +01:00
|
|
|
path_prefix (char const *prefix, char const *path)
|
2011-12-27 00:30:23 +00:00
|
|
|
{
|
2012-02-04 11:50:53 -07:00
|
|
|
/* We already know prefix[0] and path[0] are '/'. */
|
|
|
|
|
prefix++;
|
|
|
|
|
path++;
|
|
|
|
|
|
|
|
|
|
/* '/' is the prefix of everything except '//' (since we know '//'
|
|
|
|
|
is only present after canonicalization if it is distinct). */
|
|
|
|
|
if (!*prefix)
|
|
|
|
|
return *path != '/';
|
|
|
|
|
|
|
|
|
|
/* Likewise, '//' is a prefix of any double-slash path. */
|
|
|
|
|
if (*prefix == '/' && !prefix[1])
|
|
|
|
|
return *path == '/';
|
|
|
|
|
|
|
|
|
|
/* Any other prefix has a non-slash portion. */
|
2011-12-27 00:30:23 +00:00
|
|
|
while (*prefix && *path)
|
|
|
|
|
{
|
|
|
|
|
if (*prefix != *path)
|
|
|
|
|
break;
|
|
|
|
|
prefix++;
|
|
|
|
|
path++;
|
|
|
|
|
}
|
|
|
|
|
return (!*prefix && (*path == '/' || !*path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2021-04-11 18:23:21 +01:00
|
|
|
isdir (char const *path)
|
2011-12-27 00:30:23 +00:00
|
|
|
{
|
|
|
|
|
struct stat sb;
|
|
|
|
|
if (stat (path, &sb) != 0)
|
2016-10-15 23:10:35 +01:00
|
|
|
die (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (path));
|
2011-12-27 00:30:23 +00:00
|
|
|
return S_ISDIR (sb.st_mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2021-04-11 18:23:21 +01:00
|
|
|
process_path (char const *fname, int can_mode)
|
2011-12-27 00:30:23 +00:00
|
|
|
{
|
|
|
|
|
char *can_fname = realpath_canon (fname, can_mode);
|
|
|
|
|
if (!can_fname)
|
|
|
|
|
{
|
|
|
|
|
if (verbose)
|
2015-11-01 18:53:26 +00:00
|
|
|
error (0, errno, "%s", quotef (fname));
|
2011-12-27 00:30:23 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-22 20:34:57 +00:00
|
|
|
if (!can_relative_to
|
|
|
|
|
|| (can_relative_base && !path_prefix (can_relative_base, can_fname))
|
|
|
|
|
|| (can_relative_to && !relpath (can_fname, can_relative_to, NULL, 0)))
|
2012-01-25 19:49:50 +00:00
|
|
|
fputs (can_fname, stdout);
|
|
|
|
|
|
|
|
|
|
putchar (use_nuls ? '\0' : '\n');
|
2011-12-27 00:30:23 +00:00
|
|
|
|
|
|
|
|
free (can_fname);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main (int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
bool ok = true;
|
|
|
|
|
int can_mode = CAN_ALL_BUT_LAST;
|
2021-04-11 18:23:21 +01:00
|
|
|
char const *relative_to = NULL;
|
|
|
|
|
char const *relative_base = NULL;
|
2011-12-27 00:30:23 +00:00
|
|
|
|
|
|
|
|
initialize_main (&argc, &argv);
|
|
|
|
|
set_program_name (argv[0]);
|
|
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
|
bindtextdomain (PACKAGE, LOCALEDIR);
|
|
|
|
|
textdomain (PACKAGE);
|
|
|
|
|
|
|
|
|
|
atexit (close_stdout);
|
|
|
|
|
|
2021-06-26 18:23:52 -07:00
|
|
|
while (true)
|
2011-12-27 00:30:23 +00:00
|
|
|
{
|
|
|
|
|
int c = getopt_long (argc, argv, "eLmPqsz", longopts, NULL);
|
|
|
|
|
if (c == -1)
|
|
|
|
|
break;
|
|
|
|
|
switch (c)
|
|
|
|
|
{
|
|
|
|
|
case 'e':
|
|
|
|
|
can_mode &= ~CAN_MODE_MASK;
|
|
|
|
|
can_mode |= CAN_EXISTING;
|
|
|
|
|
break;
|
|
|
|
|
case 'm':
|
|
|
|
|
can_mode &= ~CAN_MODE_MASK;
|
|
|
|
|
can_mode |= CAN_MISSING;
|
|
|
|
|
break;
|
|
|
|
|
case 'L':
|
|
|
|
|
can_mode |= CAN_NOLINKS;
|
|
|
|
|
logical = true;
|
|
|
|
|
break;
|
|
|
|
|
case 's':
|
|
|
|
|
can_mode |= CAN_NOLINKS;
|
|
|
|
|
logical = false;
|
|
|
|
|
break;
|
|
|
|
|
case 'P':
|
|
|
|
|
can_mode &= ~CAN_NOLINKS;
|
|
|
|
|
logical = false;
|
|
|
|
|
break;
|
|
|
|
|
case 'q':
|
|
|
|
|
verbose = false;
|
|
|
|
|
break;
|
|
|
|
|
case 'z':
|
|
|
|
|
use_nuls = true;
|
|
|
|
|
break;
|
|
|
|
|
case RELATIVE_TO_OPTION:
|
|
|
|
|
relative_to = optarg;
|
|
|
|
|
break;
|
|
|
|
|
case RELATIVE_BASE_OPTION:
|
|
|
|
|
relative_base = optarg;
|
|
|
|
|
break;
|
|
|
|
|
case_GETOPT_HELP_CHAR;
|
|
|
|
|
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
|
|
|
|
|
default:
|
|
|
|
|
usage (EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (optind >= argc)
|
|
|
|
|
{
|
|
|
|
|
error (0, 0, _("missing operand"));
|
|
|
|
|
usage (EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (relative_base && !relative_to)
|
2012-03-14 13:42:59 -06:00
|
|
|
relative_to = relative_base;
|
2011-12-27 00:30:23 +00:00
|
|
|
|
|
|
|
|
bool need_dir = (can_mode & CAN_MODE_MASK) == CAN_EXISTING;
|
|
|
|
|
if (relative_to)
|
|
|
|
|
{
|
|
|
|
|
can_relative_to = realpath_canon (relative_to, can_mode);
|
|
|
|
|
if (!can_relative_to)
|
2016-10-15 23:10:35 +01:00
|
|
|
die (EXIT_FAILURE, errno, "%s", quotef (relative_to));
|
2011-12-27 00:30:23 +00:00
|
|
|
if (need_dir && !isdir (can_relative_to))
|
2016-10-15 23:10:35 +01:00
|
|
|
die (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_to));
|
2011-12-27 00:30:23 +00:00
|
|
|
}
|
2012-03-14 13:42:59 -06:00
|
|
|
if (relative_base == relative_to)
|
|
|
|
|
can_relative_base = can_relative_to;
|
|
|
|
|
else if (relative_base)
|
2011-12-27 00:30:23 +00:00
|
|
|
{
|
2012-03-14 13:42:59 -06:00
|
|
|
char *base = realpath_canon (relative_base, can_mode);
|
|
|
|
|
if (!base)
|
2016-10-15 23:10:35 +01:00
|
|
|
die (EXIT_FAILURE, errno, "%s", quotef (relative_base));
|
2012-03-14 13:42:59 -06:00
|
|
|
if (need_dir && !isdir (base))
|
2016-10-15 23:10:35 +01:00
|
|
|
die (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_base));
|
2012-03-14 13:42:59 -06:00
|
|
|
/* --relative-to is a no-op if it does not have --relative-base
|
|
|
|
|
as a prefix */
|
|
|
|
|
if (path_prefix (base, can_relative_to))
|
|
|
|
|
can_relative_base = base;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
free (base);
|
|
|
|
|
can_relative_base = can_relative_to;
|
|
|
|
|
can_relative_to = NULL;
|
|
|
|
|
}
|
2011-12-27 00:30:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (; optind < argc; ++optind)
|
|
|
|
|
ok &= process_path (argv[optind], can_mode);
|
|
|
|
|
|
maint: prefer 'return status;' to 'exit (status);' in 'main'
* build-aux/gen-single-binary.sh: Don't use ATTRIBUTE_NORETURN
for main functions.
* src/base64.c, src/basename.c, src/cat.c, src/chcon.c, src/chgrp.c:
* src/chmod.c, src/chown.c, src/chroot.c, src/cksum.c, src/comm.c:
* src/cp.c, src/csplit.c, src/cut.c, src/date.c, src/dd.c, src/df.c:
* src/dircolors.c, src/dirname.c, src/du.c, src/echo.c, src/env.c:
* src/expand.c, src/expr.c, src/factor.c, src/fmt.c, src/fold.c:
* src/getlimits.c, src/groups.c, src/head.c, src/hostid.c:
* src/hostname.c, src/id.c, src/install.c, src/join.c, src/kill.c:
* src/link.c, src/ln.c, src/logname.c, src/ls.c, src/make-prime-list.c:
* src/md5sum.c, src/mkdir.c, src/mkfifo.c, src/mknod.c, src/mktemp.c:
* src/mv.c, src/nice.c, src/nl.c, src/nohup.c, src/nproc.c:
* src/numfmt.c, src/od.c, src/paste.c, src/pathchk.c, src/pinky.c:
* src/pr.c, src/printenv.c, src/printf.c, src/ptx.c, src/pwd.c:
* src/readlink.c, src/realpath.c, src/rm.c, src/rmdir.c, src/runcon.c:
* src/seq.c, src/shred.c, src/shuf.c, src/sleep.c, src/sort.c:
* src/split.c, src/stat.c, src/stdbuf.c, src/stty.c, src/sum.c:
* src/sync.c, src/tac.c, src/tail.c, src/tee.c, src/timeout.c:
* src/touch.c, src/tr.c, src/true.c, src/truncate.c, src/tsort.c:
* src/tty.c, src/uname.c, src/unexpand.c, src/uniq.c, src/unlink.c:
* src/uptime.c, src/users.c, src/wc.c, src/who.c, src/whoami.c:
In 'main' functions, Prefer 'return status;' to 'exit (status);'.
* src/coreutils-arch.c (_single_binary_main_uname)
(_single_binary_main_arch):
* src/coreutils-dir.c, src/coreutils-vdir.c (_single_binary_main_ls)
(_single_binary_main_dir, _single_binary_main_vdir):
Omit ATTRIBUTE_NORETURN. Return a value.
* src/coreutils.c (SINGLE_BINARY_PROGRAM): Omit ATTRIBUTE_NORETURN.
(launch_program): Now static.
* src/dd.c (finish_up): New function.
(quit, main): Use it.
* src/getlimits.c (main): Return a proper exit status.
* src/test.c (test_main_return): New macro.
(main): Use it.
* src/logname.c, src/nohup.c, src/whoami.c:
Use 'error' to simplify exit status in 'main' function.
* src/yes.c (main): Use 'return' rather than 'error' to exit,
so that GCC doesn't suggest ATTRIBUTE_NORETURN.
2014-09-08 16:31:14 -07:00
|
|
|
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
2011-12-27 00:30:23 +00:00
|
|
|
}
|