logo

utils-std

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

offline_realpath.c (3354B)


  1. // Based on the realpath(3) function from musl
  2. // Copyright © 2005-2020 Rich Felker, et al.
  3. // SPDX-License-Identifier: MIT
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "fs.h"
  6. #include <errno.h>
  7. #include <limits.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <unistd.h>
  11. static size_t
  12. slash_len(const char *s)
  13. {
  14. const char *s0 = s;
  15. while(*s == '/')
  16. s++;
  17. return s - s0;
  18. }
  19. static char *
  20. __strchrnul(const char *s, int c)
  21. {
  22. c = (unsigned char)c;
  23. if(!c) return (char *)s + strlen(s);
  24. for(; *s && *(unsigned char *)s != c; s++)
  25. ;
  26. return (char *)s;
  27. }
  28. // realpath(3) but without checking for symlinks
  29. char *
  30. offline_realpath(const char *restrict filename, char *restrict resolved)
  31. {
  32. char stack[PATH_MAX + 1];
  33. char output[PATH_MAX];
  34. size_t p, q, l, l0, nup = 0;
  35. int check_dir = 0;
  36. if(!filename)
  37. {
  38. errno = EINVAL;
  39. return 0;
  40. }
  41. l = strnlen(filename, sizeof stack);
  42. if(!l)
  43. {
  44. errno = ENOENT;
  45. return 0;
  46. }
  47. if(l >= PATH_MAX) goto toolong;
  48. p = sizeof stack - l - 1;
  49. q = 0;
  50. memcpy(stack + p, filename, l + 1);
  51. /* Main loop. Each iteration pops the next part from stack of
  52. * remaining path components and consumes any slashes that follow.
  53. * If not a link, it's moved to output; if a link, contents are
  54. * pushed to the stack. */
  55. for(;; p += slash_len(stack + p))
  56. {
  57. /* If stack starts with /, the whole component is / or //
  58. * and the output state must be reset. */
  59. if(stack[p] == '/')
  60. {
  61. check_dir = 0;
  62. nup = 0;
  63. q = 0;
  64. output[q++] = '/';
  65. p++;
  66. /* Initial // is special. */
  67. if(stack[p] == '/' && stack[p + 1] != '/') output[q++] = '/';
  68. continue;
  69. }
  70. char *z = __strchrnul(stack + p, '/');
  71. l0 = l = z - (stack + p);
  72. if(!l && !check_dir) break;
  73. /* Skip any . component but preserve check_dir status. */
  74. if(l == 1 && stack[p] == '.')
  75. {
  76. p += l;
  77. continue;
  78. }
  79. /* Copy next component onto output at least temporarily, to
  80. * call readlink, but wait to advance output position until
  81. * determining it's not a link. */
  82. if(q && output[q - 1] != '/')
  83. {
  84. if(!p) goto toolong;
  85. stack[--p] = '/';
  86. l++;
  87. }
  88. if(q + l >= PATH_MAX) goto toolong;
  89. memcpy(output + q, stack + p, l);
  90. output[q + l] = 0;
  91. p += l;
  92. int up = 0;
  93. if(l0 == 2 && stack[p - 2] == '.' && stack[p - 1] == '.')
  94. {
  95. up = 1;
  96. /* Any non-.. path components we could cancel start
  97. * after nup repetitions of the 3-byte string "../";
  98. * if there are none, accumulate .. components to
  99. * later apply to cwd, if needed. */
  100. if(q <= 3 * nup)
  101. {
  102. nup++;
  103. q += l;
  104. continue;
  105. }
  106. }
  107. check_dir = 0;
  108. if(up)
  109. {
  110. while(q && output[q - 1] != '/')
  111. q--;
  112. if(q > 1 && (q > 2 || output[0] != '/')) q--;
  113. continue;
  114. }
  115. if(l0) q += l;
  116. check_dir = stack[p];
  117. }
  118. output[q] = 0;
  119. if(output[0] != '/')
  120. {
  121. if(!getcwd(stack, sizeof stack)) return 0;
  122. l = strlen(stack);
  123. /* Cancel any initial .. components. */
  124. p = 0;
  125. while(nup--)
  126. {
  127. while(l > 1 && stack[l - 1] != '/')
  128. l--;
  129. if(l > 1) l--;
  130. p += 2;
  131. if(p < q) p++;
  132. }
  133. if(q - p && stack[l - 1] != '/') stack[l++] = '/';
  134. if(l + (q - p) + 1 >= PATH_MAX) goto toolong;
  135. memmove(output + l, output + p, q - p + 1);
  136. memcpy(output, stack, l);
  137. q = l + q - p;
  138. }
  139. if(resolved)
  140. return memcpy(resolved, output, q + 1);
  141. else
  142. return strdup(output);
  143. toolong:
  144. errno = ENAMETOOLONG;
  145. return 0;
  146. }