commit: fcef9eaeca0535b8ad1e4663a1cdc6cb6d98dace
parent 8fdc3db142061459332c70560869525262ce79e3
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Sat, 9 Nov 2024 13:14:50 +0100
cmd/pwd: Add support for -L and -P options
Diffstat:
3 files changed, 148 insertions(+), 18 deletions(-)
diff --git a/cmd/pwd.1 b/cmd/pwd.1
@@ -9,11 +9,30 @@
.Nd print working directory
.Sh SYNOPSIS
.Nm
+.Op Fl L Ns | Ns Fl P
.Sh DESCRIPTION
.Nm
prints the full path of the current working directory.
+.Sh OPTIONS
+.Bl -tag -width _L
+.It Fl L
+(default)
+Use the
+.Ev PWD
+environment variable
+if it contains an absolute pathname of the current directory,
+and doesn't contains dot or dot-dot components.
+.It Fl P
+Writes current directory, without any components referring to a symbolic link.
+.El
+.Pp
+If both
+.Fl L
+and
+.Fl P
+are passed, the last one applies.
.Sh EXIT STATUS
-Zero.
+.Ex -std
.Sh SEE ALSO
.Xr getcwd 3
.Sh STANDARDS
diff --git a/cmd/pwd.c b/cmd/pwd.c
@@ -3,34 +3,135 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include <stdio.h> /* puts, perror, printf */
-#include <unistd.h> /* getcwd() */
+#include <limits.h> // PATH_MAX
+#include <stdbool.h>
+#include <stdio.h> // puts, perror, printf
+#include <stdlib.h> // getenv
+#include <sys/stat.h>
+#include <unistd.h> // getcwd
+
+const char *argv0 = "pwd";
+
+enum pwd
+{
+ PWD_L = 0,
+ PWD_P = 1,
+};
+
+static void
+usage()
+{
+ fputs("Usage: pwd [-L|-P]\n", stderr);
+}
+
+static bool
+is_absolute(char *path)
+{
+ if(!path) return false;
+ if(path[0] != '/') return false;
+
+ size_t dotlen = 0;
+ for(size_t i = 1; path[i] != '\0'; i++)
+ {
+ switch(path[i])
+ {
+ case '.':
+ dotlen++;
+ break;
+ case '/':
+ if(dotlen == 1 || dotlen == 2) return false;
+
+ dotlen = 0;
+ break;
+ default:
+ dotlen = 0;
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool
+is_pwd(char *path)
+{
+ if(!is_absolute(path)) return false;
+
+ struct stat path_status;
+ if(stat(path, &path_status) < 0) return false;
+
+ struct stat dot_status;
+ if(stat(".", &dot_status) < 0) return false;
+
+ if(path_status.st_dev != dot_status.st_dev) return false;
+ if(path_status.st_ino != dot_status.st_ino) return false;
+
+ return true;
+}
int
main(int argc, char *argv[])
{
- char pwd[BUFSIZ];
+ enum pwd mode = PWD_L;
- if(argc != 1)
+ int c = -1;
+ while((c = getopt(argc, argv, ":LP")) != -1)
{
- fputs("usage: pwd\n", stderr);
- return 1;
+ switch(c)
+ {
+ case 'L':
+ mode = PWD_L;
+ break;
+ case 'P':
+ mode = PWD_P;
+ break;
+ case ':':
+ fprintf(stderr, "%s: error: Missing operand for option '-%c'\n", argv0, optopt);
+ usage();
+ return 1;
+ case '?':
+ fprintf(stderr, "%s: error: Unrecognised option '-%c'\n", argv0, optopt);
+ usage();
+ return 1;
+ }
}
- if(getcwd(pwd, sizeof(pwd)) == NULL)
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 0)
{
- perror("pwd: error: getcwd");
+ usage();
return 1;
}
- else
+
+ if(mode == PWD_L)
{
- if(puts(pwd) < 0)
- {
- return 1;
- }
- else
+ char *pwd = getenv("PWD");
+ if(is_pwd(pwd))
{
+ if(puts(pwd) < 0)
+ {
+ perror("pwd: error: puts");
+ return 1;
+ }
+
return 0;
}
}
+
+ char pwd[PATH_MAX] = "";
+ if(getcwd(pwd, sizeof(pwd)) == NULL)
+ {
+ perror("pwd: error: getcwd");
+ return 1;
+ }
+
+ if(puts(pwd) < 0)
+ {
+ perror("pwd: error: puts");
+ return 1;
+ }
+
+ return 0;
}
diff --git a/test-cmd/pwd.sh b/test-cmd/pwd.sh
@@ -4,14 +4,24 @@
WD="$(dirname "$0")"
target="$(realpath "$WD/../cmd/pwd")"
-plans=3
+plans=6
. "$(dirname "$0")/tap.sh"
t noargs '' "${PWD?}
"
-t --exit=1 usage '-H' 'usage: pwd
-'
+t --exit=1 usage '-H' "pwd: error: Unrecognised option '-H'
+Usage: pwd [-L|-P]
+"
+
+PWD=foo t PWD=foo '' "${PWD?}
+"
+
+PWD=/$PWD t 'PWD=/$PWD' '' "/${PWD?}
+"
+
+PWD=//foo t 'PWD=//foo' '' "${PWD?}
+"
if command -v mktemp >/dev/null 2>/dev/null
then