logo

utils-std

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

realpath.c (4410B)


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