/* 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