logo

utils-std

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

realpath.c (4638B)


  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. int c = -1;
  81. while((c = getopt(argc, argv, ":Eemnsz")) != -1)
  82. {
  83. switch(c)
  84. {
  85. case 'E':
  86. must_exists = false;
  87. break;
  88. case 'e':
  89. must_exists = true;
  90. break;
  91. case 'n':
  92. offset_sep = 1;
  93. break;
  94. case 's':
  95. offline = true;
  96. break;
  97. case 'z':
  98. sep = '\0';
  99. break;
  100. case ':':
  101. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  102. usage_realpath();
  103. return 1;
  104. case '?':
  105. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  106. usage_realpath();
  107. return 1;
  108. }
  109. }
  110. argv += optind;
  111. argc -= optind;
  112. if(argc == 0)
  113. {
  114. fprintf(stderr, "%s: error: Expected one file as argument, got 0\n", argv0);
  115. usage_realpath();
  116. return 1;
  117. }
  118. for(int i = 0; i < argc; i++)
  119. {
  120. if(print_realpath(argv[i]) != 0) return 1;
  121. if(i != argc - offset_sep) printf("%c", sep);
  122. }
  123. return 0;
  124. }
  125. static void
  126. usage_readlink(void)
  127. {
  128. fprintf(stderr, "Usage: readlink [-f|-e] [-n|-z] file...\n");
  129. }
  130. static int
  131. main_readlink(int argc, char *argv[])
  132. {
  133. must_exists = true;
  134. bool canonicalize = false;
  135. int offset_sep = 0;
  136. int c = -1;
  137. while((c = getopt(argc, argv, ":femnz")) != -1)
  138. {
  139. switch(c)
  140. {
  141. case 'f':
  142. canonicalize = true;
  143. must_exists = false;
  144. break;
  145. case 'e':
  146. must_exists = true;
  147. break;
  148. case 'n':
  149. offset_sep = 1;
  150. break;
  151. case 'z':
  152. sep = '\0';
  153. break;
  154. case ':':
  155. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  156. usage_readlink();
  157. return 1;
  158. case '?':
  159. fprintf(stderr, "%s: error: Unrecognised option: '-%c'\n", argv0, optopt);
  160. usage_readlink();
  161. return 1;
  162. }
  163. }
  164. argv += optind;
  165. argc -= optind;
  166. if(argc == 0)
  167. {
  168. fprintf(stderr, "%s: error: Expected one file as argument, got 0\n", argv0);
  169. usage_readlink();
  170. return 1;
  171. }
  172. for(int i = 0; i < argc; i++)
  173. {
  174. char *path = argv[i];
  175. if(canonicalize)
  176. {
  177. if(print_realpath(path) != 0) return 1;
  178. if(i != argc) printf("%c", sep);
  179. continue;
  180. }
  181. char buf[PATH_MAX] = "";
  182. if(readlink(path, buf, sizeof(buf) - 1) < 0)
  183. {
  184. fprintf(stderr,
  185. "%s: error: Failed reading symbolic link of '%s': %s\n",
  186. argv0,
  187. path,
  188. strerror(errno));
  189. return 1;
  190. }
  191. printf("%s", buf);
  192. if(i != argc - offset_sep) printf("%c", sep);
  193. }
  194. return 0;
  195. }
  196. int
  197. main(int argc, char *argv[])
  198. {
  199. argv0 = static_basename(argv[0]);
  200. if(strcmp(argv0, "realpath") == 0) return main_realpath(argc, argv);
  201. if(strcmp(argv0, "readlink") == 0) return main_readlink(argc, argv);
  202. fprintf(stderr, "%s: error: Unknown utility '%s', expected realpath or readlink\n", argv0, argv0);
  203. return 1;
  204. }