From 3e5a6644c8cbbfa43be070b34c770b0fb620523d Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Wed, 16 Oct 2013 20:17:45 +0200 Subject: [PATCH] Improve implementation of pwd(1). --- utils/pwd.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 117 insertions(+), 6 deletions(-) diff --git a/utils/pwd.cpp b/utils/pwd.cpp index e7fc50cf..ed02c931 100644 --- a/utils/pwd.cpp +++ b/utils/pwd.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2013. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -20,14 +20,125 @@ *******************************************************************************/ +#include +#include #include +#include +#include #include -int main(int /*argc*/, char* /*argv*/[]) +#if !defined(VERSIONSTR) +#define VERSIONSTR "unknown version" +#endif + +static bool nul_or_slash(char c) { - const size_t WDSIZE = 4096; - char wd[WDSIZE]; - if ( !getcwd(wd, WDSIZE) ) { perror("getcwd"); return 1; } - printf("%s\n", wd); + return !c || c == '/'; +} + +static bool next_elem_is_dot_or_dot_dot(const char* path) +{ + return (path[0] == '.' && nul_or_slash(path[1])) || + (path[0] == '.' && path[1] == '.' && nul_or_slash(path[2])); +} + +static bool is_path_absolute(const char* path) +{ + size_t index = 0; + if ( path[index++] != '/' ) + return false; + while ( path[index] ) + { + if ( next_elem_is_dot_or_dot_dot(path) ) + return false; + while ( !nul_or_slash(path[index]) ) + index++; + if ( path[index] == '/' ) + index++; + } + return true; +} + +static void usage(FILE* fp, const char* argv0) +{ + fprintf(fp, "Usage: %s [OPTION]...\n", argv0); + fprintf(fp, "Print the full filename of the current working directory.\n"); + fprintf(fp, "\n"); + fprintf(fp, " -L, --logical use PWD from environment, even if it contains symlinks\n"); + fprintf(fp, " -P, --physical avoid all symlinks\n"); + fprintf(fp, " --help display this help and exit\n"); + fprintf(fp, " --usage display this help and exit\n"); + fprintf(fp, " --version output version information and exit\n"); +} + +static void help(FILE* fp, const char* argv0) +{ + usage(fp, argv0); +} + +static void version(FILE* fp, const char* argv0) +{ + fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR); + fprintf(fp, "License GPLv3+: GNU GPL version 3 or later .\n"); + fprintf(fp, "This is free software: you are free to change and redistribute it.\n"); + fprintf(fp, "There is NO WARRANTY, to the extent permitted by law.\n"); +} + +int main(int argc, char* argv[]) +{ + const char* argv0 = argv[0]; + bool physical = false; + for ( int i = 0; i < argc; i++ ) + { + const char* arg = argv[i]; + if ( arg[0] != '-' || !arg[1] ) + continue; + argv[i] = NULL; + if ( !strcmp(arg, "--") ) + break; + if ( arg[1] != '-' ) + { + while ( char c = *++arg ) switch ( c ) + { + case 'L': physical = false; break; + case 'P': physical = true; break; + default: + fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c); + usage(stderr, argv0); + exit(1); + } + } + else if ( !strcmp(arg, "--help") ) + help(stdout, argv0), exit(0); + else if ( !strcmp(arg, "--usage") ) + usage(stdout, argv0), exit(0); + else if ( !strcmp(arg, "--version") ) + version(stdout, argv0), exit(0); + else if ( !strcmp(arg, "--logical") ) + physical = false; + else if ( !strcmp(arg, "--physical") ) + physical = true; + else + { + fprintf(stderr, "%s: unknown option: %s\n", argv0, arg); + usage(stderr, argv0); + exit(1); + } + } + + // The get_current_dir_name function will use the PWD variable if it is + // accurate, so we'll need to unset it if it is inappropriate to use it. + if ( const char* pwd = getenv("PWD") ) + { + if ( physical || !is_path_absolute(pwd) ) + unsetenv(pwd); + } + + char* pwd = get_current_dir_name(); + if ( !pwd ) + error(1, errno, "get_current_dir_name"); + + printf("%s\n", pwd); + return 0; }