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