tweak comment
[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 performs 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    In any case, any getcwd replacement must *not* exceed the PATH_MAX
264    limitation.  Otherwise, functions like `chdir' would fail with
265    ENAMETOOLONG.
266
267    FIXME-maybe: if find_dir_entry fails due to permissions, try getcwd,
268    in case the unreadable directory is close enough to the root that
269    getcwd works from there.  */
270
271 static void
272 robust_getcwd (struct Path *path)
273 {
274   size_t height = 1;
275   struct dev_ino dev_ino_buf;
276   struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
277   struct stat dot_sb;
278
279   if (root_dev_ino == NULL)
280     error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
281            quote ("/"));
282
283   if (stat (".", &dot_sb) < 0)
284     error (EXIT_FAILURE, errno, _("failed to stat %s"), quote ("."));
285
286   while (1)
287     {
288       /* If we've reached the root, we're done.  */
289       if (SAME_INODE (dot_sb, *root_dev_ino))
290         break;
291
292       find_dir_entry (&dot_sb, path, height++);
293     }
294
295   if (path->start[0] == '\0')
296     path_prepend (path, "/", 1);
297 }
298
299 int
300 main (int argc, char **argv)
301 {
302   char *wd;
303
304   initialize_main (&argc, &argv);
305   program_name = argv[0];
306   setlocale (LC_ALL, "");
307   bindtextdomain (PACKAGE, LOCALEDIR);
308   textdomain (PACKAGE);
309
310   atexit (close_stdout);
311
312   parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
313                       usage, AUTHORS, (char const *) NULL);
314   if (getopt_long (argc, argv, "", NULL, NULL) != -1)
315     usage (EXIT_FAILURE);
316
317   if (optind < argc)
318     error (0, 0, _("ignoring non-option arguments"));
319
320   wd = xgetcwd ();
321   if (wd != NULL)
322     {
323       puts (wd);
324       free (wd);
325     }
326   else
327     {
328       struct Path *path = path_init ();
329       robust_getcwd (path);
330       puts (path->start);
331       path_free (path);
332     }
333
334   exit (EXIT_SUCCESS);
335 }