* src/dd.c (flags): noatime and nofollow now depend on
[coreutils/bo.git] / src / pwd.c
blob4f16b738e25f3b08c7da9865cd57c19be4cbfe97
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 2, or (at your option)
7 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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 #include <config.h>
19 #include <getopt.h>
20 #include <stdio.h>
21 #include <sys/types.h>
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"
31 /* The official name of this program (e.g., no `g' prefix). */
32 #define PROGRAM_NAME "pwd"
34 #define AUTHORS "Jim Meyering"
36 struct file_name
38 char *buf;
39 size_t n_alloc;
40 char *start;
43 /* The name this program was run with. */
44 char *program_name;
46 void
47 usage (int status)
49 if (status != EXIT_SUCCESS)
50 fprintf (stderr, _("Try `%s --help' for more information.\n"),
51 program_name);
52 else
54 printf (_("Usage: %s [OPTION]\n"), program_name);
55 fputs (_("\
56 Print the full filename of the current working directory.\n\
57 \n\
58 "), stdout);
59 fputs (HELP_OPTION_DESCRIPTION, stdout);
60 fputs (VERSION_OPTION_DESCRIPTION, stdout);
61 printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
62 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
64 exit (status);
67 static void
68 file_name_free (struct file_name *p)
70 free (p->buf);
71 free (p);
74 static struct file_name *
75 file_name_init (void)
77 struct file_name *p = xmalloc (sizeof *p);
79 /* Start with a buffer larger than PATH_MAX, but beware of systems
80 on which PATH_MAX is very large -- e.g., INT_MAX. */
81 p->n_alloc = MIN (2 * PATH_MAX, 32 * 1024);
83 p->buf = xmalloc (p->n_alloc);
84 p->start = p->buf + (p->n_alloc - 1);
85 p->start[0] = '\0';
86 return p;
89 /* Prepend the name S of length S_LEN, to the growing file_name, P. */
90 static void
91 file_name_prepend (struct file_name *p, char const *s, size_t s_len)
93 size_t n_free = p->start - p->buf;
94 if (n_free < 1 + s_len)
96 size_t half = p->n_alloc + 1 + s_len;
97 /* Use xnmalloc+free rather than xnrealloc, since with the latter
98 we'd end up copying the data twice: once via realloc, then again
99 to align it with the end of the new buffer. With xnmalloc, we
100 copy it only once. */
101 char *q = xnmalloc (2, half);
102 size_t n_used = p->n_alloc - n_free;
103 p->start = q + 2 * half - n_used;
104 memcpy (p->start, p->buf + n_free, n_used);
105 free (p->buf);
106 p->buf = q;
107 p->n_alloc = 2 * half;
110 p->start -= 1 + s_len;
111 p->start[0] = '/';
112 memcpy (p->start + 1, s, s_len);
115 /* Return a string (malloc'd) consisting of N `/'-separated ".." components. */
116 static char *
117 nth_parent (size_t n)
119 char *buf = xnmalloc (3, n);
120 char *p = buf;
121 size_t i;
123 for (i = 0; i < n; i++)
125 memcpy (p, "../", 3);
126 p += 3;
128 p[-1] = '\0';
129 return buf;
132 /* Determine the basename of the current directory, where DOT_SB is the
133 result of lstat'ing "." and prepend that to the file name in *FILE_NAME.
134 Find the directory entry in `..' that matches the dev/i-node of DOT_SB.
135 Upon success, update *DOT_SB with stat information of `..', chdir to `..',
136 and prepend "/basename" to FILE_NAME.
137 Otherwise, exit with a diagnostic.
138 PARENT_HEIGHT is the number of levels `..' is above the starting directory.
139 The first time this function is called (from the initial directory),
140 PARENT_HEIGHT is 1. This is solely for diagnostics.
141 Exit nonzero upon error. */
143 static void
144 find_dir_entry (struct stat *dot_sb, struct file_name *file_name,
145 size_t parent_height)
147 DIR *dirp;
148 int fd;
149 struct stat parent_sb;
150 bool use_lstat;
151 bool found;
153 dirp = opendir ("..");
154 if (dirp == NULL)
155 error (EXIT_FAILURE, errno, _("cannot open directory %s"),
156 quote (nth_parent (parent_height)));
158 fd = dirfd (dirp);
159 if ((0 <= fd ? fchdir (fd) : chdir ("..")) < 0)
160 error (EXIT_FAILURE, errno, _("failed to chdir to %s"),
161 quote (nth_parent (parent_height)));
163 if ((0 <= fd ? fstat (fd, &parent_sb) : stat (".", &parent_sb)) < 0)
164 error (EXIT_FAILURE, errno, _("failed to stat %s"),
165 quote (nth_parent (parent_height)));
167 /* If parent and child directory are on different devices, then we
168 can't rely on d_ino for useful i-node numbers; use lstat instead. */
169 use_lstat = (parent_sb.st_dev != dot_sb->st_dev);
171 found = false;
172 while (1)
174 struct dirent const *dp;
175 struct stat ent_sb;
176 ino_t ino;
178 errno = 0;
179 if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL)
181 if (errno)
183 /* Save/restore errno across closedir call. */
184 int e = errno;
185 closedir (dirp);
186 errno = e;
188 /* Arrange to give a diagnostic after exiting this loop. */
189 dirp = NULL;
191 break;
194 ino = D_INO (dp);
196 if (ino == NOT_AN_INODE_NUMBER || use_lstat)
198 if (lstat (dp->d_name, &ent_sb) < 0)
200 /* Skip any entry we can't stat. */
201 continue;
203 ino = ent_sb.st_ino;
206 if (ino != dot_sb->st_ino)
207 continue;
209 /* If we're not crossing a device boundary, then a simple i-node
210 match is enough. */
211 if ( ! use_lstat || ent_sb.st_dev == dot_sb->st_dev)
213 file_name_prepend (file_name, dp->d_name, _D_EXACT_NAMLEN (dp));
214 found = true;
215 break;
219 if (dirp == NULL || closedir (dirp) != 0)
221 /* Note that this diagnostic serves for both readdir
222 and closedir failures. */
223 error (EXIT_FAILURE, errno, _("reading directory %s"),
224 quote (nth_parent (parent_height)));
227 if ( ! found)
228 error (EXIT_FAILURE, 0,
229 _("couldn't find directory entry in %s with matching i-node"),
230 quote (nth_parent (parent_height)));
232 *dot_sb = parent_sb;
235 /* Construct the full, absolute name of the current working
236 directory and store it in *FILE_NAME.
237 The getcwd function performs nearly the same task, but is typically
238 unable to handle names longer than PATH_MAX. This function has
239 no such limitation. However, this function *can* fail due to
240 permission problems or a lack of memory, while Linux's getcwd
241 function works regardless of restricted permissions on parent
242 directories. Upon failure, give a diagnostic and exit nonzero.
244 Note: although this function is similar to getcwd, it has a fundamental
245 difference in that it gives a diagnostic and exits upon failure.
246 I would have liked a function that did not exit, and that could be
247 used as a getcwd replacement. Unfortunately, considering all of
248 the information the caller would require in order to produce good
249 diagnostics, it doesn't seem worth the added complexity.
250 In any case, any getcwd replacement must *not* exceed the PATH_MAX
251 limitation. Otherwise, functions like `chdir' would fail with
252 ENAMETOOLONG.
254 FIXME-maybe: if find_dir_entry fails due to permissions, try getcwd,
255 in case the unreadable directory is close enough to the root that
256 getcwd works from there. */
258 static void
259 robust_getcwd (struct file_name *file_name)
261 size_t height = 1;
262 struct dev_ino dev_ino_buf;
263 struct dev_ino *root_dev_ino = get_root_dev_ino (&dev_ino_buf);
264 struct stat dot_sb;
266 if (root_dev_ino == NULL)
267 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
268 quote ("/"));
270 if (stat (".", &dot_sb) < 0)
271 error (EXIT_FAILURE, errno, _("failed to stat %s"), quote ("."));
273 while (1)
275 /* If we've reached the root, we're done. */
276 if (SAME_INODE (dot_sb, *root_dev_ino))
277 break;
279 find_dir_entry (&dot_sb, file_name, height++);
282 /* See if a leading slash is needed; file_name_prepend adds one. */
283 if (file_name->start[0] == '\0')
284 file_name_prepend (file_name, "", 0);
288 main (int argc, char **argv)
290 char *wd;
292 initialize_main (&argc, &argv);
293 program_name = argv[0];
294 setlocale (LC_ALL, "");
295 bindtextdomain (PACKAGE, LOCALEDIR);
296 textdomain (PACKAGE);
298 atexit (close_stdout);
300 parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
301 usage, AUTHORS, (char const *) NULL);
302 if (getopt_long (argc, argv, "", NULL, NULL) != -1)
303 usage (EXIT_FAILURE);
305 if (optind < argc)
306 error (0, 0, _("ignoring non-option arguments"));
308 wd = xgetcwd ();
309 if (wd != NULL)
311 puts (wd);
312 free (wd);
314 else
316 struct file_name *file_name = file_name_init ();
317 robust_getcwd (file_name);
318 puts (file_name->start);
319 file_name_free (file_name);
322 exit (EXIT_SUCCESS);