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