1 /* pwd - print current directory
2 Copyright (C) 1994-1997, 1999-2008 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 proper_name ("Jim Meyering")
45 if (status != EXIT_SUCCESS)
46 fprintf (stderr, _("Try `%s --help' for more information.\n"),
50 printf (_("Usage: %s [OPTION]\n"), program_name);
52 Print the full filename of the current working directory.\n\
55 fputs (HELP_OPTION_DESCRIPTION, stdout);
56 fputs (VERSION_OPTION_DESCRIPTION, stdout);
57 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
58 emit_bug_reporting_address ();
64 file_name_free (struct file_name *p)
70 static struct file_name *
73 struct file_name *p = xmalloc (sizeof *p);
75 /* Start with a buffer larger than PATH_MAX, but beware of systems
76 on which PATH_MAX is very large -- e.g., INT_MAX. */
77 p->n_alloc = MIN (2 * PATH_MAX, 32 * 1024);
79 p->buf = xmalloc (p->n_alloc);
80 p->start = p->buf + (p->n_alloc - 1);
85 /* Prepend the name S of length S_LEN, to the growing file_name, P. */
87 file_name_prepend (struct file_name *p, char const *s, size_t s_len)
89 size_t n_free = p->start - p->buf;
90 if (n_free < 1 + s_len)
92 size_t half = p->n_alloc + 1 + s_len;
93 /* Use xnmalloc+free rather than xnrealloc, since with the latter
94 we'd end up copying the data twice: once via realloc, then again
95 to align it with the end of the new buffer. With xnmalloc, we
97 char *q = xnmalloc (2, half);
98 size_t n_used = p->n_alloc - n_free;
99 p->start = q + 2 * half - n_used;
100 memcpy (p->start, p->buf + n_free, n_used);
103 p->n_alloc = 2 * half;
106 p->start -= 1 + s_len;
108 memcpy (p->start + 1, s, s_len);
111 /* Return a string (malloc'd) consisting of N `/'-separated ".." components. */
113 nth_parent (size_t n)
115 char *buf = xnmalloc (3, n);
119 for (i = 0; i < n; i++)
121 memcpy (p, "../", 3);
128 /* Determine the basename of the current directory, where DOT_SB is the
129 result of lstat'ing "." and prepend that to the file name in *FILE_NAME.
130 Find the directory entry in `..' that matches the dev/i-node of DOT_SB.
131 Upon success, update *DOT_SB with stat information of `..', chdir to `..',
132 and prepend "/basename" to FILE_NAME.
133 Otherwise, exit with a diagnostic.
134 PARENT_HEIGHT is the number of levels `..' is above the starting directory.
135 The first time this function is called (from the initial directory),
136 PARENT_HEIGHT is 1. This is solely for diagnostics.
137 Exit nonzero upon error. */
140 find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
141 size_t parent_height)
145 struct stat parent_sb;
149 dirp = opendir ("..");
151 error (EXIT_FAILURE, errno, _("cannot open directory %s"),
152 quote (nth_parent (parent_height)));
155 if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)
156 error (EXIT_FAILURE, errno, _("failed to chdir to %s"),
157 quote (nth_parent (parent_height)));
159 if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)
160 error (EXIT_FAILURE, errno, _("failed to stat %s"),
161 quote (nth_parent (parent_height)));
163 /* If parent and child directory are on different devices, then we
164 can't rely on d_ino for useful i-node numbers; use lstat instead. */
165 use_lstat = (parent_sb.st_dev != dot_sb->st_dev);
170 struct dirent const *dp;
175 if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
179 /* Save/restore errno across closedir call. */
184 /* Arrange to give a diagnostic after exiting this loop. */
192 if (ino == NOT_AN_INODE_NUMBER || use_lstat)
194 if (lstat (dp->d_name, &ent_sb) < 0)
196 /* Skip any entry we can't stat. */
202 if (ino != dot_sb->st_ino)
205 /* If we're not crossing a device boundary, then a simple i-node
207 if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)
209 file_name_prepend (file_name, dp->d_name, _D_EXACT_NAMLEN (dp));
215 if (dirp == NULL || closedir (dirp) != 0)
217 /* Note that this diagnostic serves for both readdir
218 and closedir failures. */
219 error (EXIT_FAILURE, errno, _("reading directory %s"),
220 quote (nth_parent (parent_height)));
224 error (EXIT_FAILURE, 0,
225 _("couldn't find directory entry in %s with matching i-node"),
226 quote (nth_parent (parent_height)));
231 /* Construct the full, absolute name of the current working
232 directory and store it in *FILE_NAME.
233 The getcwd function performs nearly the same task, but is typically
234 unable to handle names longer than PATH_MAX. This function has
235 no such limitation. However, this function *can* fail due to
236 permission problems or a lack of memory, while Linux's getcwd
237 function works regardless of restricted permissions on parent
238 directories. Upon failure, give a diagnostic and exit nonzero.
240 Note: although this function is similar to getcwd, it has a fundamental
241 difference in that it gives a diagnostic and exits upon failure.
242 I would have liked a function that did not exit, and that could be
243 used as a getcwd replacement. Unfortunately, considering all of
244 the information the caller would require in order to produce good
245 diagnostics, it doesn't seem worth the added complexity.
246 In any case, any getcwd replacement must *not* exceed the PATH_MAX
247 limitation. Otherwise, functions like `chdir' would fail with
250 FIXME-maybe: if find_dir_entry fails due to permissions, try getcwd,
251 in case the unreadable directory is close enough to the root that
252 getcwd works from there. */
255 robust_getcwd (struct file_name *file_name)
258 struct dev_ino dev_ino_buf;
259 struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
262 if (root_dev_ino == NULL)
263 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
266 if (stat (".", &dot_sb) < 0)
267 error (EXIT_FAILURE, errno, _("failed to stat %s"), quote ("."));
271 /* If we've reached the root, we're done. */
272 if (SAME_INODE (dot_sb, *root_dev_ino))
275 find_dir_entry (&dot_sb, file_name, height++);
278 /* See if a leading slash is needed; file_name_prepend adds one. */
279 if (file_name->start[0] == '\0')
280 file_name_prepend (file_name, "", 0);
284 main (int argc, char **argv)
288 initialize_main (&argc, &argv);
289 set_program_name (argv[0]);
290 setlocale (LC_ALL, "");
291 bindtextdomain (PACKAGE, LOCALEDIR);
292 textdomain (PACKAGE);
294 atexit (close_stdout);
296 parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
297 usage, AUTHORS, (char const *) NULL);
298 if (getopt_long (argc, argv, "", NULL, NULL) != -1)
299 usage (EXIT_FAILURE);
302 error (0, 0, _("ignoring non-option arguments"));
312 struct file_name *file_name = file_name_init ();
313 robust_getcwd (file_name);
314 puts (file_name->start);
315 file_name_free (file_name);