logo

utils-std

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

realpath.c (4715B)


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