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