/* pwd - print current directory                                                This is the pwd utility
   Copyright (C) 1994-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
                                                                                
#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 "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
#include "root-dev-ino.h"                                                       ...!includes auto-comment......!includes auto-comment...
#include "xgetcwd.h"                                                            ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "pwd"                                                      Line 30
                                                                                
#define AUTHORS proper_name ("Jim Meyering")                                    Line 32
                                                                                
struct file_name                                                                Line 34
{                                                                               
  char *buf;                                                                    Line 36
  size_t n_alloc;                                                               Line 37
  char *start;                                                                  Line 38
};                                                                              Block 1
                                                                                
static struct option const longopts[] =                                         Line 41
{                                                                               
  {"logical", no_argument, NULL, 'L'},                                          Line 43
  {"physical", no_argument, NULL, 'P'},                                         Line 44
  {GETOPT_HELP_OPTION_DECL},                                                    Line 45
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 46
  {NULL, 0, NULL, 0}                                                            Line 47
};                                                                              Block 2
                                                                                
void                                                                            Line 50
usage (int status)                                                              Line 51
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 53
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 55
    {                                                                           
      printf (_("Usage: %s [OPTION]...\n"), program_name);                      Line 57
      fputs (_("\                                                               Line 58
Print the full filename of the current working directory.\n\                    Line 59
\n\                                                                             
"), stdout);                                                                    Line 61
      fputs (_("\                                                               Line 62
  -L, --logical   use PWD from environment, even if it contains symlinks\n\     Line 63
  -P, --physical  avoid all symlinks\n\                                         Line 64
"), stdout);                                                                    Line 65
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 66
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 67
      fputs (_("\n\                                                             Line 68
If no option is specified, -P is assumed.\n\                                    Line 69
"), stdout);                                                                    Line 70
      printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);                             Line 71
      emit_ancillary_info (PROGRAM_NAME);                                       Line 72
    }                                                                           
  exit (status);                                                                Line 74
}                                                                               Block 3
                                                                                
static void                                                                     Line 77
file_name_free (struct file_name *p)                                            Line 78
{                                                                               
  free (p->buf);                                                                Line 80
  free (p);                                                                     Line 81
}                                                                               Block 4
                                                                                
static struct file_name *                                                       Line 84
file_name_init (void)                                                           Line 85
{                                                                               
  struct file_name *p = xmalloc (sizeof *p);                                    Line 87
                                                                                
  /* Start with a buffer larger than PATH_MAX, but beware of systems            
     on which PATH_MAX is very large -- e.g., INT_MAX.  */                      
  p->n_alloc = MIN (2 * PATH_MAX, 32 * 1024);                                   Line 91
                                                                                
  p->buf = xmalloc (p->n_alloc);                                                Line 93
  p->start = p->buf + (p->n_alloc - 1);                                         Line 94
  p->start[0] = '\0';                                                           Line 95
  return p;                                                                     Line 96
}                                                                               Block 5
                                                                                
/* Prepend the name S of length S_LEN, to the growing file_name, P.  */         
static void                                                                     Line 100
file_name_prepend (struct file_name *p, char const *s, size_t s_len)            Line 101
{                                                                               
  size_t n_free = p->start - p->buf;                                            Line 103
  if (n_free < 1 + s_len)                                                       Line 104
    {                                                                           
      size_t half = p->n_alloc + 1 + s_len;                                     Line 106
      /* Use xnmalloc+free rather than xnrealloc, since with the latter         
         we'd end up copying the data twice: once via realloc, then again       
         to align it with the end of the new buffer.  With xnmalloc, we         
         copy it only once.  */                                                 
      char *q = xnmalloc (2, half);                                             Line 111
      size_t n_used = p->n_alloc - n_free;                                      Line 112
      p->start = q + 2 * half - n_used;                                         Line 113
      memcpy (p->start, p->buf + n_free, n_used);                               Line 114
      free (p->buf);                                                            Line 115
      p->buf = q;                                                               Line 116
      p->n_alloc = 2 * half;                                                    Line 117
    }                                                                           
                                                                                
  p->start -= 1 + s_len;                                                        Line 120
  p->start[0] = '/';                                                            Line 121
  memcpy (p->start + 1, s, s_len);                                              Line 122
}                                                                               Block 6
                                                                                
/* Return a string (malloc'd) consisting of N '/'-separated ".." components.  */
static char *                                                                   Line 126
nth_parent (size_t n)                                                           Line 127
{                                                                               
  char *buf = xnmalloc (3, n);                                                  Line 129
  char *p = buf;                                                                Line 130
                                                                                
  for (size_t i = 0; i < n; i++)                                                Line 132
    {                                                                           
      memcpy (p, "../", 3);                                                     Line 134
      p += 3;                                                                   Line 135
    }                                                                           
  p[-1] = '\0';                                                                 Line 137
  return buf;                                                                   Line 138
}                                                                               Block 7
                                                                                
/* Determine the basename of the current directory, where DOT_SB is the         
   result of lstat'ing "." and prepend that to the file name in *FILE_NAME.     
   Find the directory entry in '..' that matches the dev/i-node of DOT_SB.      
   Upon success, update *DOT_SB with stat information of '..', chdir to '..',   
   and prepend "/basename" to FILE_NAME.                                        
   Otherwise, exit with a diagnostic.                                           
   PARENT_HEIGHT is the number of levels '..' is above the starting directory.  
   The first time this function is called (from the initial directory),         
   PARENT_HEIGHT is 1.  This is solely for diagnostics.                         
   Exit nonzero upon error.  */                                                 
                                                                                
static void                                                                     Line 152
find_dir_entry (struct stat *dot_sb, struct file_name *file_name,               Line 153
                size_t parent_height)                                           Line 154
{                                                                               
  DIR *dirp;                                                                    Line 156
  int fd;                                                                       Line 157
  struct stat parent_sb;                                                        Line 158
  bool use_lstat;                                                               Line 159
  bool found;                                                                   Line 160
                                                                                
  dirp = opendir ("..");                                                        Line 162
  if (dirp == NULL)                                                             Line 163
    die (EXIT_FAILURE, errno, _("cannot open directory %s"),                    Line 164
         quote (nth_parent (parent_height)));                                   Line 165
                                                                                
  fd = dirfd (dirp);                                                            Line 167
  if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)                               Line 168
    die (EXIT_FAILURE, errno, _("failed to chdir to %s"),                       Line 169
         quote (nth_parent (parent_height)));                                   Line 170
                                                                                
  if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)          Line 172...!syscalls auto-comment......!syscalls auto-comment...
    die (EXIT_FAILURE, errno, _("failed to stat %s"),                           Line 173
         quote (nth_parent (parent_height)));                                   Line 174
                                                                                
  /* If parent and child directory are on different devices, then we            
     can't rely on d_ino for useful i-node numbers; use lstat instead.  */      
  use_lstat = (parent_sb.st_dev != dot_sb->st_dev);                             Line 178
                                                                                
  found = false;                                                                Line 180
  while (1)                                                                     Line 181
    {                                                                           
      struct dirent const *dp;                                                  Line 183
      struct stat ent_sb;                                                       Line 184
      ino_t ino;                                                                Line 185
                                                                                
      errno = 0;                                                                Line 187
      if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)                Line 188
        {                                                                       
          if (errno)                                                            Line 190
            {                                                                   
              /* Save/restore errno across closedir call.  */                   
              int e = errno;                                                    Line 193
              closedir (dirp);                                                  Line 194
              errno = e;                                                        Line 195
                                                                                
              /* Arrange to give a diagnostic after exiting this loop.  */      
              dirp = NULL;                                                      Line 198
            }                                                                   
          break;                                                                Line 200
        }                                                                       
                                                                                
      ino = D_INO (dp);                                                         Line 203
                                                                                
      if (ino == NOT_AN_INODE_NUMBER || use_lstat)                              Line 205
        {                                                                       
          if (lstat (dp->d_name, &ent_sb) < 0)                                  Line 207...!syscalls auto-comment...
            {                                                                   
              /* Skip any entry we can't stat.  */                              
              continue;                                                         Line 210
            }                                                                   
          ino = ent_sb.st_ino;                                                  Line 212
        }                                                                       
                                                                                
      if (ino != dot_sb->st_ino)                                                Line 215
        continue;                                                               Line 216
                                                                                
      /* If we're not crossing a device boundary, then a simple i-node          
         match is enough.  */                                                   
      if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)                      Line 220
        {                                                                       
          file_name_prepend (file_name, dp->d_name, _D_EXACT_NAMLEN (dp));      Line 222
          found = true;                                                         Line 223
          break;                                                                Line 224
        }                                                                       
    }                                                                           
                                                                                
  if (dirp == NULL || closedir (dirp) != 0)                                     Line 228
    {                                                                           
      /* Note that this diagnostic serves for both readdir                      
         and closedir failures.  */                                             
      die (EXIT_FAILURE, errno, _("reading directory %s"),                      Line 232
           quote (nth_parent (parent_height)));                                 Line 233
    }                                                                           
                                                                                
  if ( ! found)                                                                 Line 236
    die (EXIT_FAILURE, 0,                                                       Line 237
         _("couldn't find directory entry in %s with matching i-node"),         Line 238
         quote (nth_parent (parent_height)));                                   Line 239
                                                                                
  *dot_sb = parent_sb;                                                          Line 241
}                                                                               Block 8
                                                                                
/* Construct the full, absolute name of the current working                     
   directory and store it in *FILE_NAME.                                        
   The getcwd function performs nearly the same task, but is typically          
   unable to handle names longer than PATH_MAX.  This function has              
   no such limitation.  However, this function *can* fail due to                
   permission problems or a lack of memory, while GNU/Linux's getcwd            
   function works regardless of restricted permissions on parent                
   directories.  Upon failure, give a diagnostic and exit nonzero.              
                                                                                
   Note: although this function is similar to getcwd, it has a fundamental      
   difference in that it gives a diagnostic and exits upon failure.             
   I would have liked a function that did not exit, and that could be           
   used as a getcwd replacement.  Unfortunately, considering all of             
   the information the caller would require in order to produce good            
   diagnostics, it doesn't seem worth the added complexity.                     
   In any case, any getcwd replacement must *not* exceed the PATH_MAX           
   limitation.  Otherwise, functions like 'chdir' would fail with               
   ENAMETOOLONG.                                                                
                                                                                
   FIXME-maybe: if find_dir_entry fails due to permissions, try getcwd,         
   in case the unreadable directory is close enough to the root that            
   getcwd works from there.  */                                                 
                                                                                
static void                                                                     Line 267
robust_getcwd (struct file_name *file_name)                                     Line 268
{                                                                               
  size_t height = 1;                                                            Line 270
  struct dev_ino dev_ino_buf;                                                   Line 271
  struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);               Line 272
  struct stat dot_sb;                                                           Line 273
                                                                                
  if (root_dev_ino == NULL)                                                     Line 275
    die (EXIT_FAILURE, errno, _("failed to get attributes of %s"),              Line 276
         quoteaf ("/"));                                                        Line 277
                                                                                
  if (stat (".", &dot_sb) < 0)                                                  Line 279...!syscalls auto-comment...
    die (EXIT_FAILURE, errno, _("failed to stat %s"), quoteaf ("."));           Line 280
                                                                                
  while (1)                                                                     Line 282
    {                                                                           
      /* If we've reached the root, we're done.  */                             
      if (SAME_INODE (dot_sb, *root_dev_ino))                                   Line 285
        break;                                                                  Line 286
                                                                                
      find_dir_entry (&dot_sb, file_name, height++);                            Line 288
    }                                                                           
                                                                                
  /* See if a leading slash is needed; file_name_prepend adds one.  */          
  if (file_name->start[0] == '\0')                                              Line 292
    file_name_prepend (file_name, "", 0);                                       Line 293
}                                                                               Block 9
                                                                                
                                                                                
/* Return PWD from the environment if it is acceptable for 'pwd -L'             
   output, otherwise NULL.  */                                                  
static char *                                                                   Line 299
logical_getcwd (void)                                                           Line 300
{                                                                               
  struct stat st1;                                                              Line 302
  struct stat st2;                                                              Line 303
  char *wd = getenv ("PWD");                                                    Line 304
  char *p;                                                                      Line 305
                                                                                
  /* Textual validation first.  */                                              
  if (!wd || wd[0] != '/')                                                      Line 308
    return NULL;                                                                Line 309
  p = wd;                                                                       Line 310
  while ((p = strstr (p, "/.")))                                                Line 311
    {                                                                           
      if (!p[2] || p[2] == '/'                                                  Line 313
          || (p[2] == '.' && (!p[3] || p[3] == '/')))                           Line 314
        return NULL;                                                            Line 315
      p++;                                                                      Line 316
    }                                                                           
                                                                                
  /* System call validation.  */                                                
  if (stat (wd, &st1) == 0 && stat (".", &st2) == 0 && SAME_INODE (st1, st2))   Line 320...!syscalls auto-comment...
    return wd;                                                                  Line 321
  return NULL;                                                                  Line 322
}                                                                               Block 10
                                                                                
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 327
{                                                                               
  char *wd;                                                                     Line 329
  /* POSIX requires a default of -L, but most scripts expect -P.                
     Currently shells default to -L, while stand-alone                          
     pwd implementations default to -P.  */                                     
  bool logical = (getenv ("POSIXLY_CORRECT") != NULL);                          Line 333
                                                                                
  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 343
    {                                                                           
      int c = getopt_long (argc, argv, "LP", longopts, NULL);                   Line 345
      if (c == -1)                                                              Line 346
        break;                                                                  Line 347
      switch (c)                                                                Line 348
        {                                                                       
        case 'L':                                                               Line 350
          logical = true;                                                       Line 351
          break;                                                                Line 352
        case 'P':                                                               Line 353
          logical = false;                                                      Line 354
          break;                                                                Line 355
                                                                                
        case_GETOPT_HELP_CHAR;                                                  Line 357
                                                                                
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 359
                                                                                
        default:                                                                Line 361
          usage (EXIT_FAILURE);                                                 Line 362
        }                                                                       
    }                                                                           
                                                                                
  if (optind < argc)                                                            Line 366
    error (0, 0, _("ignoring non-option arguments"));                           Line 367
                                                                                
  if (logical)                                                                  Line 369
    {                                                                           
      wd = logical_getcwd ();                                                   Line 371
      if (wd)                                                                   Line 372
        {                                                                       
          puts (wd);                                                            Line 374
          return EXIT_SUCCESS;                                                  Line 375
        }                                                                       
    }                                                                           
                                                                                
  wd = xgetcwd ();                                                              Line 379
  if (wd != NULL)                                                               Line 380
    {                                                                           
      puts (wd);                                                                Line 382
      free (wd);                                                                Line 383
    }                                                                           
  else                                                                          Line 385
    {                                                                           
      struct file_name *file_name = file_name_init ();                          Line 387
      robust_getcwd (file_name);                                                Line 388
      puts (file_name->start);                                                  Line 389
      file_name_free (file_name);                                               Line 390
    }                                                                           
                                                                                
  return EXIT_SUCCESS;                                                          Line 393
}                                                                               Block 11