1 /* pwd - print current directory
2 Copyright (C) 1994-2024 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 <https://www.gnu.org/licenses/>. */
20 #include <sys/types.h>
24 #include "root-dev-ino.h"
27 /* The official name of this program (e.g., no 'g' prefix). */
28 #define PROGRAM_NAME "pwd"
30 #define AUTHORS proper_name ("Jim Meyering")
39 static struct option
const longopts
[] =
41 {"logical", no_argument
, nullptr, 'L'},
42 {"physical", no_argument
, nullptr, 'P'},
43 {GETOPT_HELP_OPTION_DECL
},
44 {GETOPT_VERSION_OPTION_DECL
},
45 {nullptr, 0, nullptr, 0}
51 if (status
!= EXIT_SUCCESS
)
55 printf (_("Usage: %s [OPTION]...\n"), program_name
);
57 Print the full filename of the current working directory.\n\
61 -L, --logical use PWD from environment, even if it contains symlinks\n\
62 -P, --physical avoid all symlinks\n\
64 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
65 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
67 If no option is specified, -P is assumed.\n\
69 printf (USAGE_BUILTIN_WARNING
, PROGRAM_NAME
);
70 emit_ancillary_info (PROGRAM_NAME
);
76 file_name_free (struct file_name
*p
)
82 static struct file_name
*
85 struct file_name
*p
= xmalloc (sizeof *p
);
87 /* Start with a buffer larger than PATH_MAX, but beware of systems
88 on which PATH_MAX is very large -- e.g., INT_MAX. */
89 p
->n_alloc
= MIN (2 * PATH_MAX
, 32 * 1024);
91 p
->buf
= xmalloc (p
->n_alloc
);
92 p
->start
= p
->buf
+ (p
->n_alloc
- 1);
97 /* Prepend the name S of length S_LEN, to the growing file_name, P. */
99 file_name_prepend (struct file_name
*p
, char const *s
, size_t s_len
)
101 size_t n_free
= p
->start
- p
->buf
;
102 if (n_free
< 1 + s_len
)
104 size_t half
= p
->n_alloc
+ 1 + s_len
;
105 /* Use xnmalloc+free rather than xnrealloc, since with the latter
106 we'd end up copying the data twice: once via realloc, then again
107 to align it with the end of the new buffer. With xnmalloc, we
108 copy it only once. */
109 char *q
= xnmalloc (2, half
);
110 size_t n_used
= p
->n_alloc
- n_free
;
111 p
->start
= q
+ 2 * half
- n_used
;
112 memcpy (p
->start
, p
->buf
+ n_free
, n_used
);
115 p
->n_alloc
= 2 * half
;
118 p
->start
-= 1 + s_len
;
120 memcpy (p
->start
+ 1, s
, s_len
);
123 /* Return a string (malloc'd) consisting of N '/'-separated ".." components. */
125 nth_parent (size_t n
)
127 char *buf
= xnmalloc (3, n
);
130 for (size_t i
= 0; i
< n
; i
++)
132 memcpy (p
, "../", 3);
139 /* Determine the basename of the current directory, where DOT_SB is the
140 result of lstat'ing "." and prepend that to the file name in *FILE_NAME.
141 Find the directory entry in '..' that matches the dev/i-node of DOT_SB.
142 Upon success, update *DOT_SB with stat information of '..', chdir to '..',
143 and prepend "/basename" to FILE_NAME.
144 Otherwise, exit with a diagnostic.
145 PARENT_HEIGHT is the number of levels '..' is above the starting directory.
146 The first time this function is called (from the initial directory),
147 PARENT_HEIGHT is 1. This is solely for diagnostics.
148 Exit nonzero upon error. */
151 find_dir_entry (struct stat
*dot_sb
, struct file_name
*file_name
,
152 size_t parent_height
)
156 struct stat parent_sb
;
160 dirp
= opendir ("..");
162 error (EXIT_FAILURE
, errno
, _("cannot open directory %s"),
163 quote (nth_parent (parent_height
)));
166 if ((0 <= fd
? fchdir (fd
) : chdir ("..")) < 0)
167 error (EXIT_FAILURE
, errno
, _("failed to chdir to %s"),
168 quote (nth_parent (parent_height
)));
170 if ((0 <= fd
? fstat (fd
, &parent_sb
) : stat (".", &parent_sb
)) < 0)
171 error (EXIT_FAILURE
, errno
, _("failed to stat %s"),
172 quote (nth_parent (parent_height
)));
174 /* If parent and child directory are on different devices, then we
175 can't rely on d_ino for useful i-node numbers; use lstat instead. */
176 use_lstat
= (parent_sb
.st_dev
!= dot_sb
->st_dev
);
181 struct dirent
const *dp
;
186 if ((dp
= readdir_ignoring_dot_and_dotdot (dirp
)) == nullptr)
190 /* Save/restore errno across closedir call. */
195 /* Arrange to give a diagnostic after exiting this loop. */
203 if (ino
== NOT_AN_INODE_NUMBER
|| use_lstat
)
205 if (lstat (dp
->d_name
, &ent_sb
) < 0)
207 /* Skip any entry we can't stat. */
213 if (ino
!= dot_sb
->st_ino
)
216 /* If we're not crossing a device boundary, then a simple i-node
218 if ( ! use_lstat
|| ent_sb
.st_dev
== dot_sb
->st_dev
)
220 file_name_prepend (file_name
, dp
->d_name
, _D_EXACT_NAMLEN (dp
));
226 if (dirp
== nullptr || closedir (dirp
) != 0)
228 /* Note that this diagnostic serves for both readdir
229 and closedir failures. */
230 error (EXIT_FAILURE
, errno
, _("reading directory %s"),
231 quote (nth_parent (parent_height
)));
235 error (EXIT_FAILURE
, 0,
236 _("couldn't find directory entry in %s with matching i-node"),
237 quote (nth_parent (parent_height
)));
242 /* Construct the full, absolute name of the current working
243 directory and store it in *FILE_NAME.
244 The getcwd function performs nearly the same task, but is typically
245 unable to handle names longer than PATH_MAX. This function has
246 no such limitation. However, this function *can* fail due to
247 permission problems or a lack of memory, while GNU/Linux's getcwd
248 function works regardless of restricted permissions on parent
249 directories. Upon failure, give a diagnostic and exit nonzero.
251 Note: although this function is similar to getcwd, it has a fundamental
252 difference in that it gives a diagnostic and exits upon failure.
253 I would have liked a function that did not exit, and that could be
254 used as a getcwd replacement. Unfortunately, considering all of
255 the information the caller would require in order to produce good
256 diagnostics, it doesn't seem worth the added complexity.
257 In any case, any getcwd replacement must *not* exceed the PATH_MAX
258 limitation. Otherwise, functions like 'chdir' would fail with
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. */
266 robust_getcwd (struct file_name
*file_name
)
269 struct dev_ino dev_ino_buf
;
270 struct dev_ino
*root_dev_ino
= get_root_dev_ino (&dev_ino_buf
);
273 if (root_dev_ino
== nullptr)
274 error (EXIT_FAILURE
, errno
, _("failed to get attributes of %s"),
277 if (stat (".", &dot_sb
) < 0)
278 error (EXIT_FAILURE
, errno
, _("failed to stat %s"), quoteaf ("."));
282 /* If we've reached the root, we're done. */
283 if (PSAME_INODE (&dot_sb
, root_dev_ino
))
286 find_dir_entry (&dot_sb
, file_name
, height
++);
289 /* See if a leading slash is needed; file_name_prepend adds one. */
290 if (file_name
->start
[0] == '\0')
291 file_name_prepend (file_name
, "", 0);
295 /* Return PWD from the environment if it is acceptable for 'pwd -L'
296 output, otherwise nullptr. */
298 logical_getcwd (void)
302 char *wd
= getenv ("PWD");
305 /* Textual validation first. */
306 if (!wd
|| wd
[0] != '/')
309 while ((p
= strstr (p
, "/.")))
311 if (!p
[2] || p
[2] == '/'
312 || (p
[2] == '.' && (!p
[3] || p
[3] == '/')))
317 /* System call validation. */
318 if (stat (wd
, &st1
) == 0 && stat (".", &st2
) == 0 && psame_inode (&st1
, &st2
))
325 main (int argc
, char **argv
)
328 /* POSIX requires a default of -L, but most scripts expect -P.
329 Currently shells default to -L, while stand-alone
330 pwd implementations default to -P. */
331 bool logical
= (getenv ("POSIXLY_CORRECT") != nullptr);
333 initialize_main (&argc
, &argv
);
334 set_program_name (argv
[0]);
335 setlocale (LC_ALL
, "");
336 bindtextdomain (PACKAGE
, LOCALEDIR
);
337 textdomain (PACKAGE
);
339 atexit (close_stdout
);
343 int c
= getopt_long (argc
, argv
, "LP", longopts
, nullptr);
355 case_GETOPT_HELP_CHAR
;
357 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
360 usage (EXIT_FAILURE
);
365 error (0, 0, _("ignoring non-option arguments"));
369 wd
= logical_getcwd ();
385 struct file_name
*file_name
= file_name_init ();
386 robust_getcwd (file_name
);
387 puts (file_name
->start
);
388 file_name_free (file_name
);