logo

utils-std

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

realpath.c (4634B)


  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. #define _XOPEN_SOURCE 700 // realpath is in XSI
  6. #include "../lib/fs.h"
  7. #include <errno.h>
  8. #include <limits.h> // PATH_MAX
  9. #include <stdbool.h>
  10. #include <stdio.h> // fprintf(), puts()
  11. #include <stdlib.h> // realpath()
  12. #include <string.h> // strncmp(), strnlen, strerror
  13. #include <unistd.h> // getopt
  14. static bool must_exists = false;
  15. static bool offline = false;
  16. static char *argv0 = NULL;
  17. static char sep = '\n';
  18. static int
  19. print_realpath(char *path)
  20. {
  21. char *file = NULL;
  22. if(offline)
  23. file = offline_realpath(path, NULL);
  24. else
  25. file = realpath(path, NULL);
  26. if(file)
  27. {
  28. if(printf("%s", file) < 0)
  29. {
  30. fprintf(stderr, "%s: error: Failed writing to stdout: %s\n", argv0, strerror(errno));
  31. free(file);
  32. return 1;
  33. }
  34. free(file);
  35. return 0;
  36. }
  37. if(must_exists || errno != ENOENT)
  38. {
  39. fprintf(stderr, "%s: error: Failed canonilizing \"%s\": %s\n", argv0, path, strerror(errno));
  40. return 1;
  41. }
  42. char *child = path_split_static(path, true);
  43. if(child == NULL)
  44. {
  45. // Return as if realpath just failed
  46. fprintf(stderr, "%s: error: Failed canonilizing \"%s\": %s\n", argv0, path, strerror(errno));
  47. return 1;
  48. }
  49. errno = 0;
  50. char *parent = realpath(path, NULL);
  51. if(!parent)
  52. {
  53. fprintf(stderr,
  54. "%s: error: Failed canonilizing parent of full path \"%s/%s\": %s\n",
  55. argv0,
  56. path,
  57. child,
  58. strerror(errno));
  59. return 1;
  60. }
  61. if(printf("%s/%s", parent, child) < 0)
  62. {
  63. fprintf(stderr, "%s: error: Failed writing to stdout: %s\n", argv0, strerror(errno));
  64. free(parent);
  65. return 1;
  66. }
  67. free(parent);
  68. return 0;
  69. }
  70. static void
  71. usage_realpath(void)
  72. {
  73. fprintf(stderr, "Usage: realpath [-E|-e] [-n|-z] path...\n");
  74. }
  75. static int
  76. main_realpath(int argc, char *argv[])
  77. {
  78. must_exists = false;
  79. int offset_sep = 0;
  80. for(int c = -1; (c = getopt(argc, argv, ":Eemnsz")) != -1;)
  81. {
  82. switch(c)
  83. {
  84. case 'E':
  85. must_exists = false;
  86. break;
  87. case 'e':
  88. must_exists = true;
  89. break;
  90. case 'n':
  91. offset_sep = 1;
  92. break;
  93. case 's':
  94. offline = true;
  95. break;
  96. case 'z':
  97. sep = '\0';
  98. break;
  99. case ':':
  100. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  101. usage_realpath();
  102. return 1;
  103. case '?':
  104. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  105. usage_realpath();
  106. return 1;
  107. }
  108. }
  109. argv += optind;
  110. argc -= optind;
  111. if(argc == 0)
  112. {
  113. fprintf(stderr, "%s: error: Expected one file as argument, got 0\n", argv0);
  114. usage_realpath();
  115. return 1;
  116. }
  117. for(int i = 0; i < argc; i++)
  118. {
  119. if(print_realpath(argv[i]) != 0) return 1;
  120. if(i != argc - offset_sep) printf("%c", sep);
  121. }
  122. return 0;
  123. }
  124. static void
  125. usage_readlink(void)
  126. {
  127. fprintf(stderr, "Usage: readlink [-f|-e] [-n|-z] file...\n");
  128. }
  129. static int
  130. main_readlink(int argc, char *argv[])
  131. {
  132. must_exists = true;
  133. bool canonicalize = false;
  134. int offset_sep = 0;
  135. for(int c = -1; (c = getopt(argc, argv, ":femnz")) != -1;)
  136. {
  137. switch(c)
  138. {
  139. case 'f':
  140. canonicalize = true;
  141. must_exists = false;
  142. break;
  143. case 'e':
  144. must_exists = true;
  145. break;
  146. case 'n':
  147. offset_sep = 1;
  148. break;
  149. case 'z':
  150. sep = '\0';
  151. break;
  152. case ':':
  153. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  154. usage_readlink();
  155. return 1;
  156. case '?':
  157. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  158. usage_readlink();
  159. return 1;
  160. }
  161. }
  162. argv += optind;
  163. argc -= optind;
  164. if(argc == 0)
  165. {
  166. fprintf(stderr, "%s: error: Expected one file as argument, got 0\n", argv0);
  167. usage_readlink();
  168. return 1;
  169. }
  170. for(int i = 0; i < argc; i++)
  171. {
  172. char *path = argv[i];
  173. if(canonicalize)
  174. {
  175. if(print_realpath(path) != 0) return 1;
  176. if(i != argc) printf("%c", sep);
  177. continue;
  178. }
  179. char buf[PATH_MAX] = "";
  180. if(readlink(path, buf, sizeof(buf) - 1) < 0)
  181. {
  182. fprintf(stderr,
  183. "%s: error: Failed reading symbolic link of '%s': %s\n",
  184. argv0,
  185. path,
  186. strerror(errno));
  187. return 1;
  188. }
  189. printf("%s", buf);
  190. if(i != argc - offset_sep) printf("%c", sep);
  191. }
  192. return 0;
  193. }
  194. int
  195. main(int argc, char *argv[])
  196. {
  197. argv0 = static_basename(argv[0]);
  198. if(strcmp(argv0, "realpath") == 0) return main_realpath(argc, argv);
  199. if(strcmp(argv0, "readlink") == 0) return main_readlink(argc, argv);
  200. fprintf(stderr, "%s: error: Unknown utility '%s', expected realpath or readlink\n", argv0, argv0);
  201. return 1;
  202. }