(root)/
coreutils-9.4/
src/
realpath.c
       1  /* realpath - print the resolved path
       2     Copyright (C) 2011-2023 Free Software Foundation, Inc.
       3  
       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.
       8  
       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.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Pádraig Brady.  */
      18  
      19  #include <config.h>
      20  #include <getopt.h>
      21  #include <stdio.h>
      22  #include <sys/types.h>
      23  
      24  #include "system.h"
      25  #include "canonicalize.h"
      26  #include "relpath.h"
      27  
      28  /* The official name of this program (e.g., no 'g' prefix).  */
      29  #define PROGRAM_NAME "realpath"
      30  
      31  #define AUTHORS proper_name_lite ("Padraig Brady", "P\303\241draig Brady")
      32  
      33  enum
      34  {
      35    RELATIVE_TO_OPTION = CHAR_MAX + 1,
      36    RELATIVE_BASE_OPTION
      37  };
      38  
      39  static bool verbose = true;
      40  static bool logical;
      41  static bool use_nuls;
      42  static char const *can_relative_to;
      43  static char const *can_relative_base;
      44  
      45  static struct option const longopts[] =
      46  {
      47    {"canonicalize-existing", no_argument, nullptr, 'e'},
      48    {"canonicalize-missing", no_argument, nullptr, 'm'},
      49    {"relative-to", required_argument, nullptr, RELATIVE_TO_OPTION},
      50    {"relative-base", required_argument, nullptr, RELATIVE_BASE_OPTION},
      51    {"quiet", no_argument, nullptr, 'q'},
      52    {"strip", no_argument, nullptr, 's'},
      53    {"no-symlinks", no_argument, nullptr, 's'},
      54    {"zero", no_argument, nullptr, 'z'},
      55    {"logical", no_argument, nullptr, 'L'},
      56    {"physical", no_argument, nullptr, 'P'},
      57    {GETOPT_HELP_OPTION_DECL},
      58    {GETOPT_VERSION_OPTION_DECL},
      59    {nullptr, 0, nullptr, 0}
      60  };
      61  
      62  void
      63  usage (int status)
      64  {
      65    if (status != EXIT_SUCCESS)
      66      emit_try_help ();
      67    else
      68      {
      69        printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
      70        fputs (_("\
      71  Print the resolved absolute file name;\n\
      72  all but the last component must exist\n\
      73  \n\
      74  "), stdout);
      75        fputs (_("\
      76    -e, --canonicalize-existing  all components of the path must exist\n\
      77    -m, --canonicalize-missing   no path components need exist or be a directory\
      78  \n\
      79    -L, --logical                resolve '..' components before symlinks\n\
      80    -P, --physical               resolve symlinks as encountered (default)\n\
      81    -q, --quiet                  suppress most error messages\n\
      82        --relative-to=DIR        print the resolved path relative to DIR\n\
      83        --relative-base=DIR      print absolute paths unless paths below DIR\n\
      84    -s, --strip, --no-symlinks   don't expand symlinks\n\
      85    -z, --zero                   end each output line with NUL, not newline\n\
      86  "), stdout);
      87        fputs (HELP_OPTION_DESCRIPTION, stdout);
      88        fputs (VERSION_OPTION_DESCRIPTION, stdout);
      89        emit_ancillary_info (PROGRAM_NAME);
      90      }
      91    exit (status);
      92  }
      93  
      94  /* A wrapper around canonicalize_filename_mode(),
      95     to call it twice when in LOGICAL mode.  */
      96  static char *
      97  realpath_canon (char const *fname, int can_mode)
      98  {
      99    char *can_fname = canonicalize_filename_mode (fname, can_mode);
     100    if (logical && can_fname)  /* canonicalize again to resolve symlinks.  */
     101      {
     102        can_mode &= ~CAN_NOLINKS;
     103        char *can_fname2 = canonicalize_filename_mode (can_fname, can_mode);
     104        free (can_fname);
     105        return can_fname2;
     106      }
     107    return can_fname;
     108  }
     109  
     110  /* Test whether canonical prefix is parent or match of path.  */
     111  ATTRIBUTE_PURE
     112  static bool
     113  path_prefix (char const *prefix, char const *path)
     114  {
     115    /* We already know prefix[0] and path[0] are '/'.  */
     116    prefix++;
     117    path++;
     118  
     119    /* '/' is the prefix of everything except '//' (since we know '//'
     120       is only present after canonicalization if it is distinct).  */
     121    if (!*prefix)
     122      return *path != '/';
     123  
     124    /* Likewise, '//' is a prefix of any double-slash path.  */
     125    if (*prefix == '/' && !prefix[1])
     126      return *path == '/';
     127  
     128    /* Any other prefix has a non-slash portion.  */
     129    while (*prefix && *path)
     130      {
     131        if (*prefix != *path)
     132          break;
     133        prefix++;
     134        path++;
     135      }
     136    return (!*prefix && (*path == '/' || !*path));
     137  }
     138  
     139  static bool
     140  isdir (char const *path)
     141  {
     142    struct stat sb;
     143    if (stat (path, &sb) != 0)
     144      error (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (path));
     145    return S_ISDIR (sb.st_mode);
     146  }
     147  
     148  static bool
     149  process_path (char const *fname, int can_mode)
     150  {
     151    char *can_fname = realpath_canon (fname, can_mode);
     152    if (!can_fname)
     153      {
     154        if (verbose)
     155          error (0, errno, "%s", quotef (fname));
     156        return false;
     157      }
     158  
     159    if (!can_relative_to
     160        || (can_relative_base && !path_prefix (can_relative_base, can_fname))
     161        || (can_relative_to && !relpath (can_fname, can_relative_to, nullptr, 0)))
     162      fputs (can_fname, stdout);
     163  
     164    putchar (use_nuls ? '\0' : '\n');
     165  
     166    free (can_fname);
     167  
     168    return true;
     169  }
     170  
     171  int
     172  main (int argc, char **argv)
     173  {
     174    bool ok = true;
     175    int can_mode = CAN_ALL_BUT_LAST;
     176    char const *relative_to = nullptr;
     177    char const *relative_base = nullptr;
     178  
     179    initialize_main (&argc, &argv);
     180    set_program_name (argv[0]);
     181    setlocale (LC_ALL, "");
     182    bindtextdomain (PACKAGE, LOCALEDIR);
     183    textdomain (PACKAGE);
     184  
     185    atexit (close_stdout);
     186  
     187    while (true)
     188      {
     189        int c = getopt_long (argc, argv, "eLmPqsz", longopts, nullptr);
     190        if (c == -1)
     191          break;
     192        switch (c)
     193          {
     194          case 'e':
     195            can_mode &= ~CAN_MODE_MASK;
     196            can_mode |= CAN_EXISTING;
     197            break;
     198          case 'm':
     199            can_mode &= ~CAN_MODE_MASK;
     200            can_mode |= CAN_MISSING;
     201            break;
     202          case 'L':
     203            can_mode |= CAN_NOLINKS;
     204            logical = true;
     205            break;
     206          case 's':
     207            can_mode |= CAN_NOLINKS;
     208            logical = false;
     209            break;
     210          case 'P':
     211            can_mode &= ~CAN_NOLINKS;
     212            logical = false;
     213            break;
     214          case 'q':
     215            verbose = false;
     216            break;
     217          case 'z':
     218            use_nuls = true;
     219            break;
     220          case RELATIVE_TO_OPTION:
     221            relative_to = optarg;
     222            break;
     223          case RELATIVE_BASE_OPTION:
     224            relative_base = optarg;
     225            break;
     226          case_GETOPT_HELP_CHAR;
     227          case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     228          default:
     229            usage (EXIT_FAILURE);
     230          }
     231      }
     232  
     233    if (optind >= argc)
     234      {
     235        error (0, 0, _("missing operand"));
     236        usage (EXIT_FAILURE);
     237      }
     238  
     239    if (relative_base && !relative_to)
     240      relative_to = relative_base;
     241  
     242    bool need_dir = (can_mode & CAN_MODE_MASK) == CAN_EXISTING;
     243    if (relative_to)
     244      {
     245        can_relative_to = realpath_canon (relative_to, can_mode);
     246        if (!can_relative_to)
     247          error (EXIT_FAILURE, errno, "%s", quotef (relative_to));
     248        if (need_dir && !isdir (can_relative_to))
     249          error (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_to));
     250      }
     251    if (relative_base == relative_to)
     252      can_relative_base = can_relative_to;
     253    else if (relative_base)
     254      {
     255        char *base = realpath_canon (relative_base, can_mode);
     256        if (!base)
     257          error (EXIT_FAILURE, errno, "%s", quotef (relative_base));
     258        if (need_dir && !isdir (base))
     259          error (EXIT_FAILURE, ENOTDIR, "%s", quotef (relative_base));
     260        /* --relative-to is a no-op if it does not have --relative-base
     261             as a prefix */
     262        if (path_prefix (base, can_relative_to))
     263          can_relative_base = base;
     264        else
     265          {
     266            free (base);
     267            can_relative_base = can_relative_to;
     268            can_relative_to = nullptr;
     269          }
     270      }
     271  
     272    for (; optind < argc; ++optind)
     273      ok &= process_path (argv[optind], can_mode);
     274  
     275    return ok ? EXIT_SUCCESS : EXIT_FAILURE;
     276  }