logo

utils-std

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

offline_realpath.c (3337B)


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