The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=2df923c5d2d02350abc29f61b603c5b9615b225c
commit 2df923c5d2d02350abc29f61b603c5b9615b225c Author: Dag-Erling Smørgrav <[email protected]> AuthorDate: 2026-02-10 14:29:04 +0000 Commit: Dag-Erling Smørgrav <[email protected]> CommitDate: 2026-02-10 14:29:04 +0000 pwd: Clean up and adopt POSIX semantics According to POSIX, the default should be -L. Based on code history, whoever first wrote BSD pwd(1) could not figure out how to implement -L and therefore made -P the default (and only) option. Support for -L was later added, but the default was never changed. Clean up the code, make -L the default, and rewrite getcwd_logical() to reject paths that contain dot or dot-dot, as required by POSIX. MFC after: 1 week Reviewed by: olce Differential Revision: https://reviews.freebsd.org/D55146 --- bin/pwd/pwd.1 | 4 +-- bin/pwd/pwd.c | 99 +++++++++++++++++++++++++++++++---------------------------- 2 files changed, 54 insertions(+), 49 deletions(-) diff --git a/bin/pwd/pwd.1 b/bin/pwd/pwd.1 index ce81e443d98e..8b03d060bc8e 100644 --- a/bin/pwd/pwd.1 +++ b/bin/pwd/pwd.1 @@ -29,7 +29,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 22, 2023 +.Dd Februrary 6, 2026 .Dt PWD 1 .Os .Sh NAME @@ -60,7 +60,7 @@ Display the physical current working directory (all symbolic links resolved). .El .Pp If no options are specified, the -.Fl P +.Fl L option is assumed. .Sh ENVIRONMENT Environment variables used by diff --git a/bin/pwd/pwd.c b/bin/pwd/pwd.c index a49c638b1dee..f626a2236c02 100644 --- a/bin/pwd/pwd.c +++ b/bin/pwd/pwd.c @@ -3,6 +3,7 @@ * * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. + * Copyright (c) 2026 Dag-Erling Smørgrav * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,40 +32,74 @@ #include <sys/param.h> #include <sys/stat.h> -#include <sys/types.h> #include <err.h> -#include <errno.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> -static char *getcwd_logical(void); -void usage(void); +static char * +getcwd_logical(void) +{ + struct stat log, phy; + char *pwd, *p, *q; + + /* $PWD is set and absolute */ + if ((pwd = getenv("PWD")) == NULL || *pwd != '/') + return (NULL); + /* $PWD does not contain /./ or /../ */ + for (p = pwd; *p; p = q) { + for (q = ++p; *q && *q != '/'; q++) + /* nothing */; + if ((*p == '.' && q == ++p) || + (*p == '.' && q == ++p)) + return (NULL); + } + /* $PWD refers to the current directory */ + if (stat(pwd, &log) != 0 || stat(".", &phy) != 0 || + log.st_dev != phy.st_dev || log.st_ino != phy.st_ino) + return (NULL); + return (pwd); +} + +static char * +getcwd_physical(void) +{ + static char pwd[MAXPATHLEN]; + + return (getcwd(pwd, sizeof(pwd))); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: pwd [-L | -P]\n"); + exit(1); +} int main(int argc, char *argv[]) { - int physical; - int ch; - char *p; + char *pwd; + int opt; + bool logical; - physical = 1; - while ((ch = getopt(argc, argv, "LP")) != -1) - switch (ch) { + logical = true; + while ((opt = getopt(argc, argv, "LP")) != -1) { + switch (opt) { case 'L': - physical = 0; + logical = true; break; case 'P': - physical = 1; + logical = false; break; - case '?': default: usage(); } + } argc -= optind; argv += optind; - if (argc != 0) usage(); @@ -72,40 +107,10 @@ main(int argc, char *argv[]) * If we're trying to find the logical current directory and that * fails, behave as if -P was specified. */ - if ((!physical && (p = getcwd_logical()) != NULL) || - (p = getcwd(NULL, 0)) != NULL) - printf("%s\n", p); + if ((logical && (pwd = getcwd_logical()) != NULL) || + (pwd = getcwd_physical()) != NULL) + printf("%s\n", pwd); else err(1, "."); - exit(0); } - -void __dead2 -usage(void) -{ - - (void)fprintf(stderr, "usage: pwd [-L | -P]\n"); - exit(1); -} - -static char * -getcwd_logical(void) -{ - struct stat lg, phy; - char *pwd; - - /* - * Check that $PWD is an absolute logical pathname referring to - * the current working directory. - */ - if ((pwd = getenv("PWD")) != NULL && *pwd == '/') { - if (stat(pwd, &lg) == -1 || stat(".", &phy) == -1) - return (NULL); - if (lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino) - return (pwd); - } - - errno = ENOENT; - return (NULL); -}
