/* realpath - print the resolved path This is the realpath utility
Copyright (C) 2011-2018 Free Software Foundation, Inc.
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
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Written by Pádraig Brady. */
#include <config.h> Provides system specific information
#include <getopt.h> ...!includes auto-comment...
#include <stdio.h> Provides standard I/O capability
#include <sys/types.h> Provides system data types
#include "system.h" ...!includes auto-comment...
#include "canonicalize.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "relpath.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "realpath" Line 31
#define AUTHORS proper_name ("Padraig Brady") Line 33
enum Line 35
{
RELATIVE_TO_OPTION = CHAR_MAX + 1, Line 37
RELATIVE_BASE_OPTION Line 38
}; Block 1
static bool verbose = true; Line 41
static bool logical; Line 42
static bool use_nuls; Line 43
static const char *can_relative_to; Line 44
static const char *can_relative_base; Line 45
static struct option const longopts[] = Line 47
{
{"canonicalize-existing", no_argument, NULL, 'e'}, Line 49
{"canonicalize-missing", no_argument, NULL, 'm'}, Line 50
{"relative-to", required_argument, NULL, RELATIVE_TO_OPTION}, Line 51
{"relative-base", required_argument, NULL, RELATIVE_BASE_OPTION}, Line 52
{"quiet", no_argument, NULL, 'q'}, Line 53
{"strip", no_argument, NULL, 's'}, Line 54
{"no-symlinks", no_argument, NULL, 's'}, Line 55
{"zero", no_argument, NULL, 'z'}, Line 56
{"logical", no_argument, NULL, 'L'}, Line 57
{"physical", no_argument, NULL, 'P'}, Line 58
{GETOPT_HELP_OPTION_DECL}, Line 59
{GETOPT_VERSION_OPTION_DECL}, Line 60
{NULL, 0, NULL, 0} Line 61
}; Block 2
void Line 64
usage (int status) Line 65
{
if (status != EXIT_SUCCESS) Line 67
emit_try_help (); ...!common auto-comment...
else Line 69
{
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name); Line 71
fputs (_("\ Line 72
Print the resolved absolute file name;\n\ Line 73
all but the last component must exist\n\ Line 74
\n\
"), stdout); Line 76
fputs (_("\ Line 77
-e, --canonicalize-existing all components of the path must exist\n\ Line 78
-m, --canonicalize-missing no path components need exist or be a directory\ Line 79
\n\
-L, --logical resolve '..' components before symlinks\n\ Line 81
-P, --physical resolve symlinks as encountered (default)\n\ Line 82
-q, --quiet suppress most error messages\n\ Line 83
--relative-to=DIR print the resolved path relative to DIR\n\ Line 84
--relative-base=DIR print absolute paths unless paths below DIR\n\ Line 85
-s, --strip, --no-symlinks don't expand symlinks\n\ Line 86
-z, --zero end each output line with NUL, not newline\n\ Line 87
\n\
"), stdout); Line 89
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 90
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 91
emit_ancillary_info (PROGRAM_NAME); Line 92
}
exit (status); Line 94
} Block 3
/* A wrapper around canonicalize_filename_mode(),
to call it twice when in LOGICAL mode. */
static char * Line 99
realpath_canon (const char *fname, int can_mode) Line 100
{
char *can_fname = canonicalize_filename_mode (fname, can_mode); Line 102
if (logical && can_fname) /* canonicalize again to resolve symlinks. */ Line 103
{
can_mode &= ~CAN_NOLINKS; Line 105
char *can_fname2 = canonicalize_filename_mode (can_fname, can_mode); Line 106
free (can_fname); Line 107
return can_fname2; Line 108
}
return can_fname; Line 110
} Block 4
/* Test whether canonical prefix is parent or match of path. */
static bool _GL_ATTRIBUTE_PURE Line 114
path_prefix (const char *prefix, const char *path) Line 115
{
/* We already know prefix[0] and path[0] are '/'. */
prefix++; Line 118
path++; Line 119
/* '/' is the prefix of everything except '//' (since we know '//'
is only present after canonicalization if it is distinct). */
if (!*prefix) Line 123
return *path != '/'; Line 124
/* Likewise, '//' is a prefix of any double-slash path. */
if (*prefix == '/' && !prefix[1]) Line 127
return *path == '/'; Line 128
/* Any other prefix has a non-slash portion. */
while (*prefix && *path) Line 131
{
if (*prefix != *path) Line 133
break; Line 134
prefix++; Line 135
path++; Line 136
}
return (!*prefix && (*path == '/' || !*path)); Line 138
}
static bool Line 141
isdir (const char *path) Line 142
{
struct stat sb; Line 144
if (stat (path, &sb) != 0) Line 145...!syscalls auto-comment...
die (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (path)); Line 146
return S_ISDIR (sb.st_mode); Line 147
} Block 6
static bool Line 150
process_path (const char *fname, int can_mode) Line 151
{
char *can_fname = realpath_canon (fname, can_mode); Line 153
if (!can_fname) Line 154
{
if (verbose) Line 156
error (0, errno, "%s", quotef (fname)); Line 157
return false; Line 158
}
if (!can_relative_to Line 161
|| (can_relative_base && !path_prefix (can_relative_base, can_fname)) Line 162
|| (can_relative_to && !relpath (can_fname, can_relative_to, NULL, 0))) Line 163
fputs (can_fname, stdout); Line 164
putchar (use_nuls ? '\0' : '\n'); Line 166
free (can_fname); Line 168
return true; Line 170
} Block 7
int
main (int argc, char **argv) Line 174
{
bool ok = true; Line 176
int can_mode = CAN_ALL_BUT_LAST; Line 177
const char *relative_to = NULL; Line 178
const char *relative_base = NULL; Line 179
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
while (1) Line 189
{
int c = getopt_long (argc, argv, "eLmPqsz", longopts, NULL); Line 191
if (c == -1) Line 192
break; Line 193
switch (c) Line 194
{
case 'e': Line 196
can_mode &= ~CAN_MODE_MASK; Line 197
can_mode |= CAN_EXISTING; Line 198
break; Line 199
case 'm': Line 200
can_mode &= ~CAN_MODE_MASK; Line 201
can_mode |= CAN_MISSING; Line 202
break; Line 203
case 'L': Line 204
can_mode |= CAN_NOLINKS; Line 205
logical = true; Line 206
break; Line 207
case 's': Line 208
can_mode |= CAN_NOLINKS; Line 209
logical = false; Line 210
break; Line 211
case 'P': Line 212
can_mode &= ~CAN_NOLINKS; Line 213
logical = false; Line 214
break; Line 215
case 'q': Line 216
verbose = false; Line 217
break; Line 218
case 'z': Line 219
use_nuls = true; Line 220
break; Line 221
case RELATIVE_TO_OPTION: Line 222
relative_to = optarg; Line 223
break; Line 224
case RELATIVE_BASE_OPTION: Line 225
relative_base = optarg; Line 226
break; Line 227
case_GETOPT_HELP_CHAR; Line 228
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 229
default: Line 230
usage (EXIT_FAILURE); Line 231
}
}
if (optind >= argc) Line 235
{
error (0, 0, _("missing operand")); Line 237
usage (EXIT_FAILURE); Line 238
}
if (relative_base && !relative_to) Line 241
relative_to = relative_base; Line 242
bool need_dir = (can_mode & CAN_MODE_MASK) == CAN_EXISTING; Line 244
if (relative_to) Line 245
{
can_relative_to = realpath_canon (relative_to, can_mode); Line 247
if (!can_relative_to) Line 248
die (EXIT_FAILURE, errno, "%s", quotef (relative_to)); Line 249
if (need_dir && !isdir (can_relative_to)) Line 250
die (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_to)); Line 251
}
if (relative_base == relative_to) Line 253
can_relative_base = can_relative_to; Line 254
else if (relative_base) Line 255
{
char *base = realpath_canon (relative_base, can_mode); Line 257
if (!base) Line 258
die (EXIT_FAILURE, errno, "%s", quotef (relative_base)); Line 259
if (need_dir && !isdir (base)) Line 260
die (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_base)); Line 261
/* --relative-to is a no-op if it does not have --relative-base
as a prefix */
if (path_prefix (base, can_relative_to)) Line 264
can_relative_base = base; Line 265
else Line 266
{
free (base); Line 268
can_relative_base = can_relative_to; Line 269
can_relative_to = NULL; Line 270
}
}
for (; optind < argc; ++optind) Line 274
ok &= process_path (argv[optind], can_mode); Line 275
return ok ? EXIT_SUCCESS : EXIT_FAILURE; Line 277
} Block 8