logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git
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:

Mcmd/pwd.121++++++++++++++++++++-
Mcmd/pwd.c129++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mtest-cmd/pwd.sh16+++++++++++++---
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