1 /* pwd - print current directory
2 Copyright (C) 1994-1997, 1999-2006 Free Software Foundation, Inc.
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.
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.
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/>. */
20 #include <sys/types.h>
25 #include "long-options.h"
27 #include "root-dev-ino.h"
30 /* The official name of this program (e.g., no `g' prefix). */
31 #define PROGRAM_NAME "pwd"
33 #define AUTHORS "Jim Meyering"
42 /* The name this program was run with. */
48 if (status != EXIT_SUCCESS)
49 fprintf (stderr, _("Try `%s --help' for more information.\n"),
53 printf (_("Usage: %s [OPTION]\n"), program_name);
55 Print the full filename of the current working directory.\n\
58 fputs (HELP_OPTION_DESCRIPTION, stdout);
59 fputs (VERSION_OPTION_DESCRIPTION, stdout);
60 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
61 emit_bug_reporting_address ();
67 file_name_free (struct file_name *p)
73 static struct file_name *
76 struct file_name *p = xmalloc (sizeof *p);
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 p->n_alloc = MIN (2 * PATH_MAX, 32 * 1024);
82 p->buf = xmalloc (p->n_alloc);
83 p->start = p->buf + (p->n_alloc - 1);
88 /* Prepend the name S of length S_LEN, to the growing file_name, P. */
90 file_name_prepend (struct file_name *p, char const *s, size_t s_len)
92 size_t n_free = p->start - p->buf;
93 if (n_free < 1 + s_len)
95 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
100 char *q = xnmalloc (2, half);
101 size_t n_used = p->n_alloc - n_free;
102 p->start = q + 2 * half - n_used;
103 memcpy (p->start, p->buf + n_free, n_used);
106 p->n_alloc = 2 * half;
109 p->start -= 1 + s_len;
111 memcpy (p->start + 1, s, s_len);
114 /* Return a string (malloc'd) consisting of N `/'-separated ".." components. */
116 nth_parent (size_t n)
118 char *buf = xnmalloc (3, n);
122 for (i = 0; i < n; i++)
124 memcpy (p, "../", 3);
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. */
143 find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
144 size_t parent_height)
148 struct stat parent_sb;
152 dirp = opendir ("..");
154 error (EXIT_FAILURE, errno, _("cannot open directory %s"),
155 quote (nth_parent (parent_height)));
158 if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)
159 error (EXIT_FAILURE, errno, _("failed to chdir to %s"),
160 quote (nth_parent (parent_height)));
162 if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)
163 error (EXIT_FAILURE, errno, _("failed to stat %s"),
164 quote (nth_parent (parent_height)));
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 use_lstat = (parent_sb.st_dev != dot_sb->st_dev);
173 struct dirent const *dp;
178 if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
182 /* Save/restore errno across closedir call. */
187 /* Arrange to give a diagnostic after exiting this loop. */
195 if (ino == NOT_AN_INODE_NUMBER || use_lstat)
197 if (lstat (dp->d_name, &ent_sb) < 0)
199 /* Skip any entry we can't stat. */
205 if (ino != dot_sb->st_ino)
208 /* If we're not crossing a device boundary, then a simple i-node
210 if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)
212 file_name_prepend (file_name, dp->d_name, _D_EXACT_NAMLEN (dp));
218 if (dirp == NULL || closedir (dirp) != 0)
220 /* Note that this diagnostic serves for both readdir
221 and closedir failures. */
222 error (EXIT_FAILURE, errno, _("reading directory %s"),
223 quote (nth_parent (parent_height)));
227 error (EXIT_FAILURE, 0,
228 _("couldn't find directory entry in %s with matching i-node"),
229 quote (nth_parent (parent_height)));
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.
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
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. */
258 robust_getcwd (struct file_name *file_name)
261 struct dev_ino dev_ino_buf;
262 struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
265 if (root_dev_ino == NULL)
266 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
269 if (stat (".", &dot_sb) < 0)
270 error (EXIT_FAILURE, errno, _("failed to stat %s"), quote ("."));
274 /* If we've reached the root, we're done. */
275 if (SAME_INODE (dot_sb, *root_dev_ino))
278 find_dir_entry (&dot_sb, file_name, height++);
281 /* See if a leading slash is needed; file_name_prepend adds one. */
282 if (file_name->start[0] == '\0')
283 file_name_prepend (file_name, "", 0);
287 main (int argc, char **argv)
291 initialize_main (&argc, &argv);
292 program_name = argv[0];
293 setlocale (LC_ALL, "");
294 bindtextdomain (PACKAGE, LOCALEDIR);
295 textdomain (PACKAGE);
297 atexit (close_stdout);
299 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
300 usage, AUTHORS, (char const *) NULL);
301 if (getopt_long (argc, argv, "", NULL, NULL) != -1)
302 usage (EXIT_FAILURE);
305 error (0, 0, _("ignoring non-option arguments"));
315 struct file_name *file_name = file_name_init ();
316 robust_getcwd (file_name);
317 puts (file_name->start);
318 file_name_free (file_name);