LCOV - code coverage report
Current view: top level - src - pwd.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 24 117 20.5 %
Date: 2018-01-30 Functions: 2 8 25.0 %

          Line data    Source code
       1             : /* pwd - print current directory
       2             :    Copyright (C) 1994-1997, 1999-2007 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 <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : #include <config.h>
      18             : #include <getopt.h>
      19             : #include <stdio.h>
      20             : #include <sys/types.h>
      21             : 
      22             : #include "system.h"
      23             : #include "dirfd.h"
      24             : #include "error.h"
      25             : #include "long-options.h"
      26             : #include "quote.h"
      27             : #include "root-dev-ino.h"
      28             : #include "xgetcwd.h"
      29             : 
      30             : /* The official name of this program (e.g., no `g' prefix).  */
      31             : #define PROGRAM_NAME "pwd"
      32             : 
      33             : #define AUTHORS "Jim Meyering"
      34             : 
      35             : struct file_name
      36             : {
      37             :   char *buf;
      38             :   size_t n_alloc;
      39             :   char *start;
      40             : };
      41             : 
      42             : /* The name this program was run with. */
      43             : char *program_name;
      44             : 
      45             : void
      46           8 : usage (int status)
      47             : {
      48           8 :   if (status != EXIT_SUCCESS)
      49           6 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
      50             :              program_name);
      51             :   else
      52             :     {
      53           2 :       printf (_("Usage: %s [OPTION]\n"), program_name);
      54           2 :       fputs (_("\
      55             : Print the full filename of the current working directory.\n\
      56             : \n\
      57             : "), stdout);
      58           2 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
      59           2 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
      60           2 :       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
      61           2 :       emit_bug_reporting_address ();
      62             :     }
      63           8 :   exit (status);
      64             : }
      65             : 
      66             : static void
      67           0 : file_name_free (struct file_name *p)
      68             : {
      69           0 :   free (p->buf);
      70           0 :   free (p);
      71           0 : }
      72             : 
      73             : static struct file_name *
      74           0 : file_name_init (void)
      75             : {
      76           0 :   struct file_name *p = xmalloc (sizeof *p);
      77             : 
      78             :   /* Start with a buffer larger than PATH_MAX, but beware of systems
      79             :      on which PATH_MAX is very large -- e.g., INT_MAX.  */
      80           0 :   p->n_alloc = MIN (2 * PATH_MAX, 32 * 1024);
      81             : 
      82           0 :   p->buf = xmalloc (p->n_alloc);
      83           0 :   p->start = p->buf + (p->n_alloc - 1);
      84           0 :   p->start[0] = '\0';
      85           0 :   return p;
      86             : }
      87             : 
      88             : /* Prepend the name S of length S_LEN, to the growing file_name, P.  */
      89             : static void
      90           0 : file_name_prepend (struct file_name *p, char const *s, size_t s_len)
      91             : {
      92           0 :   size_t n_free = p->start - p->buf;
      93           0 :   if (n_free < 1 + s_len)
      94             :     {
      95           0 :       size_t half = p->n_alloc + 1 + s_len;
      96             :       /* Use xnmalloc+free rather than xnrealloc, since with the latter
      97             :          we'd end up copying the data twice: once via realloc, then again
      98             :          to align it with the end of the new buffer.  With xnmalloc, we
      99             :          copy it only once.  */
     100           0 :       char *q = xnmalloc (2, half);
     101           0 :       size_t n_used = p->n_alloc - n_free;
     102           0 :       p->start = q + 2 * half - n_used;
     103           0 :       memcpy (p->start, p->buf + n_free, n_used);
     104           0 :       free (p->buf);
     105           0 :       p->buf = q;
     106           0 :       p->n_alloc = 2 * half;
     107             :     }
     108             : 
     109           0 :   p->start -= 1 + s_len;
     110           0 :   p->start[0] = '/';
     111           0 :   memcpy (p->start + 1, s, s_len);
     112           0 : }
     113             : 
     114             : /* Return a string (malloc'd) consisting of N `/'-separated ".." components.  */
     115             : static char *
     116           0 : nth_parent (size_t n)
     117             : {
     118           0 :   char *buf = xnmalloc (3, n);
     119           0 :   char *p = buf;
     120             :   size_t i;
     121             : 
     122           0 :   for (i = 0; i < n; i++)
     123             :     {
     124           0 :       memcpy (p, "../", 3);
     125           0 :       p += 3;
     126             :     }
     127           0 :   p[-1] = '\0';
     128           0 :   return buf;
     129             : }
     130             : 
     131             : /* Determine the basename of the current directory, where DOT_SB is the
     132             :    result of lstat'ing "." and prepend that to the file name in *FILE_NAME.
     133             :    Find the directory entry in `..' that matches the dev/i-node of DOT_SB.
     134             :    Upon success, update *DOT_SB with stat information of `..', chdir to `..',
     135             :    and prepend "/basename" to FILE_NAME.
     136             :    Otherwise, exit with a diagnostic.
     137             :    PARENT_HEIGHT is the number of levels `..' is above the starting directory.
     138             :    The first time this function is called (from the initial directory),
     139             :    PARENT_HEIGHT is 1.  This is solely for diagnostics.
     140             :    Exit nonzero upon error.  */
     141             : 
     142             : static void
     143           0 : find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
     144             :                 size_t parent_height)
     145             : {
     146             :   DIR *dirp;
     147             :   int fd;
     148             :   struct stat parent_sb;
     149             :   bool use_lstat;
     150             :   bool found;
     151             : 
     152           0 :   dirp = opendir ("..");
     153           0 :   if (dirp == NULL)
     154           0 :     error (EXIT_FAILURE, errno, _("cannot open directory %s"),
     155           0 :            quote (nth_parent (parent_height)));
     156             : 
     157           0 :   fd = dirfd (dirp);
     158           0 :   if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)
     159           0 :     error (EXIT_FAILURE, errno, _("failed to chdir to %s"),
     160           0 :            quote (nth_parent (parent_height)));
     161             : 
     162           0 :   if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)
     163           0 :     error (EXIT_FAILURE, errno, _("failed to stat %s"),
     164           0 :            quote (nth_parent (parent_height)));
     165             : 
     166             :   /* If parent and child directory are on different devices, then we
     167             :      can't rely on d_ino for useful i-node numbers; use lstat instead.  */
     168           0 :   use_lstat = (parent_sb.st_dev != dot_sb->st_dev);
     169             : 
     170           0 :   found = false;
     171             :   while (1)
     172           0 :     {
     173             :       struct dirent const *dp;
     174             :       struct stat ent_sb;
     175             :       ino_t ino;
     176             : 
     177           0 :       errno = 0;
     178           0 :       if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
     179             :         {
     180           0 :           if (errno)
     181             :             {
     182             :               /* Save/restore errno across closedir call.  */
     183           0 :               int e = errno;
     184           0 :               closedir (dirp);
     185           0 :               errno = e;
     186             : 
     187             :               /* Arrange to give a diagnostic after exiting this loop.  */
     188           0 :               dirp = NULL;
     189             :             }
     190           0 :           break;
     191             :         }
     192             : 
     193           0 :       ino = D_INO (dp);
     194             : 
     195           0 :       if (ino == NOT_AN_INODE_NUMBER || use_lstat)
     196             :         {
     197           0 :           if (lstat (dp->d_name, &ent_sb) < 0)
     198             :             {
     199             :               /* Skip any entry we can't stat.  */
     200           0 :               continue;
     201             :             }
     202           0 :           ino = ent_sb.st_ino;
     203             :         }
     204             : 
     205           0 :       if (ino != dot_sb->st_ino)
     206           0 :         continue;
     207             : 
     208             :       /* If we're not crossing a device boundary, then a simple i-node
     209             :          match is enough.  */
     210           0 :       if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)
     211             :         {
     212           0 :           file_name_prepend (file_name, dp->d_name, _D_EXACT_NAMLEN (dp));
     213           0 :           found = true;
     214           0 :           break;
     215             :         }
     216             :     }
     217             : 
     218           0 :   if (dirp == NULL || closedir (dirp) != 0)
     219             :     {
     220             :       /* Note that this diagnostic serves for both readdir
     221             :          and closedir failures.  */
     222           0 :       error (EXIT_FAILURE, errno, _("reading directory %s"),
     223           0 :              quote (nth_parent (parent_height)));
     224             :     }
     225             : 
     226           0 :   if ( ! found)
     227           0 :     error (EXIT_FAILURE, 0,
     228             :            _("couldn't find directory entry in %s with matching i-node"),
     229           0 :              quote (nth_parent (parent_height)));
     230             : 
     231           0 :   *dot_sb = parent_sb;
     232           0 : }
     233             : 
     234             : /* Construct the full, absolute name of the current working
     235             :    directory and store it in *FILE_NAME.
     236             :    The getcwd function performs nearly the same task, but is typically
     237             :    unable to handle names longer than PATH_MAX.  This function has
     238             :    no such limitation.  However, this function *can* fail due to
     239             :    permission problems or a lack of memory, while Linux's getcwd
     240             :    function works regardless of restricted permissions on parent
     241             :    directories.  Upon failure, give a diagnostic and exit nonzero.
     242             : 
     243             :    Note: although this function is similar to getcwd, it has a fundamental
     244             :    difference in that it gives a diagnostic and exits upon failure.
     245             :    I would have liked a function that did not exit, and that could be
     246             :    used as a getcwd replacement.  Unfortunately, considering all of
     247             :    the information the caller would require in order to produce good
     248             :    diagnostics, it doesn't seem worth the added complexity.
     249             :    In any case, any getcwd replacement must *not* exceed the PATH_MAX
     250             :    limitation.  Otherwise, functions like `chdir' would fail with
     251             :    ENAMETOOLONG.
     252             : 
     253             :    FIXME-maybe: if find_dir_entry fails due to permissions, try getcwd,
     254             :    in case the unreadable directory is close enough to the root that
     255             :    getcwd works from there.  */
     256             : 
     257             : static void
     258           0 : robust_getcwd (struct file_name *file_name)
     259             : {
     260           0 :   size_t height = 1;
     261             :   struct dev_ino dev_ino_buf;
     262           0 :   struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
     263             :   struct stat dot_sb;
     264             : 
     265           0 :   if (root_dev_ino == NULL)
     266           0 :     error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
     267             :            quote ("/"));
     268             : 
     269           0 :   if (stat (".", &dot_sb) < 0)
     270           0 :     error (EXIT_FAILURE, errno, _("failed to stat %s"), quote ("."));
     271             : 
     272             :   while (1)
     273             :     {
     274             :       /* If we've reached the root, we're done.  */
     275           0 :       if (SAME_INODE (dot_sb, *root_dev_ino))
     276           0 :         break;
     277             : 
     278           0 :       find_dir_entry (&dot_sb, file_name, height++);
     279             :     }
     280             : 
     281             :   /* See if a leading slash is needed; file_name_prepend adds one.  */
     282           0 :   if (file_name->start[0] == '\0')
     283           0 :     file_name_prepend (file_name, "", 0);
     284           0 : }
     285             : 
     286             : int
     287          19 : main (int argc, char **argv)
     288             : {
     289             :   char *wd;
     290             : 
     291             :   initialize_main (&argc, &argv);
     292          19 :   program_name = argv[0];
     293          19 :   setlocale (LC_ALL, "");
     294             :   bindtextdomain (PACKAGE, LOCALEDIR);
     295             :   textdomain (PACKAGE);
     296             : 
     297          19 :   atexit (close_stdout);
     298             : 
     299          19 :   parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
     300             :                       usage, AUTHORS, (char const *) NULL);
     301          16 :   if (getopt_long (argc, argv, "", NULL, NULL) != -1)
     302           6 :     usage (EXIT_FAILURE);
     303             : 
     304          10 :   if (optind < argc)
     305           8 :     error (0, 0, _("ignoring non-option arguments"));
     306             : 
     307          10 :   wd = xgetcwd ();
     308          10 :   if (wd != NULL)
     309             :     {
     310          10 :       puts (wd);
     311          10 :       free (wd);
     312             :     }
     313             :   else
     314             :     {
     315           0 :       struct file_name *file_name = file_name_init ();
     316           0 :       robust_getcwd (file_name);
     317           0 :       puts (file_name->start);
     318           0 :       file_name_free (file_name);
     319             :     }
     320             : 
     321          10 :   exit (EXIT_SUCCESS);
     322             : }

Generated by: LCOV version 1.10