logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

pwd.c (2634B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "../config.h"
  6. #include "../libutils/getopt_nolong.h"
  7. #include <limits.h> // PATH_MAX
  8. #include <stdbool.h>
  9. #include <stdio.h> // puts, perror, printf
  10. #include <stdlib.h> // getenv
  11. #include <sys/stat.h>
  12. #include <unistd.h> // getcwd
  13. #ifdef HAS_GETOPT_LONG
  14. #include <getopt.h>
  15. #endif
  16. const char *argv0 = "pwd";
  17. enum pwd
  18. {
  19. PWD_L = 0,
  20. PWD_P = 1,
  21. };
  22. static void
  23. usage(void)
  24. {
  25. fputs("Usage: pwd [-L|-P]\n", stderr);
  26. }
  27. static bool
  28. is_absolute(char *path)
  29. {
  30. if(!path) return false;
  31. if(path[0] != '/') return false;
  32. size_t dotlen = 0;
  33. for(size_t i = 1; path[i] != '\0'; i++)
  34. {
  35. switch(path[i])
  36. {
  37. case '.':
  38. dotlen++;
  39. break;
  40. case '/':
  41. if(dotlen == 1 || dotlen == 2) return false;
  42. dotlen = 0;
  43. break;
  44. default:
  45. dotlen = 0;
  46. break;
  47. }
  48. }
  49. return true;
  50. }
  51. static bool
  52. is_pwd(char *path)
  53. {
  54. if(!is_absolute(path)) return false;
  55. struct stat path_status;
  56. if(stat(path, &path_status) < 0) return false;
  57. struct stat dot_status;
  58. if(stat(".", &dot_status) < 0) return false;
  59. if(path_status.st_dev != dot_status.st_dev) return false;
  60. if(path_status.st_ino != dot_status.st_ino) return false;
  61. return true;
  62. }
  63. int
  64. main(int argc, char *argv[])
  65. {
  66. enum pwd mode = PWD_L;
  67. #ifdef HAS_GETOPT_LONG
  68. // Strictly for GNUisms compatibility so no long-only options
  69. // clang-format off
  70. static struct option opts[] = {
  71. {"logical", no_argument, NULL, 'L'},
  72. {"physical", no_argument, NULL, 'P'},
  73. {0, 0, 0, 0},
  74. };
  75. // clang-format on
  76. // Need + as first character to get POSIX-style option parsing
  77. for(int c = -1; (c = getopt_long(argc, argv, "+:LP", opts, NULL)) != -1;)
  78. #else
  79. for(int c = -1; (c = getopt_nolong(argc, argv, ":LP")) != -1;)
  80. #endif
  81. {
  82. switch(c)
  83. {
  84. case 'L':
  85. mode = PWD_L;
  86. break;
  87. case 'P':
  88. mode = PWD_P;
  89. break;
  90. case ':':
  91. fprintf(stderr, "%s: error: Missing operand for option '-%c'\n", argv0, optopt);
  92. usage();
  93. return 1;
  94. case '?':
  95. GETOPT_UNKNOWN_OPT
  96. usage();
  97. return 1;
  98. }
  99. }
  100. argc -= optind;
  101. argv += optind;
  102. if(argc != 0)
  103. {
  104. usage();
  105. return 1;
  106. }
  107. if(mode == PWD_L)
  108. {
  109. char *pwd = getenv("PWD");
  110. if(is_pwd(pwd))
  111. {
  112. if(puts(pwd) < 0)
  113. {
  114. perror("pwd: error: puts");
  115. return 1;
  116. }
  117. return 0;
  118. }
  119. }
  120. char pwd[PATH_MAX] = "";
  121. if(getcwd(pwd, sizeof(pwd)) == NULL)
  122. {
  123. perror("pwd: error: getcwd");
  124. return 1;
  125. }
  126. if(puts(pwd) < 0)
  127. {
  128. perror("pwd: error: puts");
  129. return 1;
  130. }
  131. return 0;
  132. }