resolver.cc 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // CODYlib -*- mode:c++ -*-
  2. // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
  3. // License: Apache v2.0
  4. // Cody
  5. #include "internal.hh"
  6. // OS
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <sys/stat.h>
  10. #include <sys/types.h>
  11. #if ((defined (__unix__) \
  12. && defined _POSIX_C_SOURCE \
  13. && (_POSIX_C_SOURCE - 0) >= 200809L) \
  14. || (defined (__Apple__) \
  15. && defined (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) \
  16. && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000))
  17. // Autoconf test?
  18. #define HAVE_FSTATAT 1
  19. #else
  20. #define HAVE_FSTATAT 0
  21. #endif
  22. // Resolver code
  23. #if __windows__
  24. inline bool IsDirSep (char c)
  25. {
  26. return c == '/' || c == '\\';
  27. }
  28. inline bool IsAbsPath (char const *str)
  29. {
  30. // IIRC windows has the concept of per-drive current directories,
  31. // which make drive-using paths confusing. Let's not get into that.
  32. return IsDirSep (str)
  33. || (((str[0] >= 'A' && str[0] <= 'Z')
  34. || (str[0] >= 'a' && str[0] <= 'z'))&& str[1] == ':');
  35. }
  36. #else
  37. inline bool IsDirSep (char c)
  38. {
  39. return c == '/';
  40. }
  41. inline bool IsAbsPath (char const *str)
  42. {
  43. return IsDirSep (str[0]);
  44. }
  45. #endif
  46. constexpr char DIR_SEPARATOR = '/';
  47. constexpr char DOT_REPLACE = ','; // Replace . directories
  48. constexpr char COLON_REPLACE = '-'; // Replace : (partition char)
  49. constexpr char const REPO_DIR[] = "cmi.cache";
  50. namespace Cody {
  51. Resolver::~Resolver ()
  52. {
  53. }
  54. char const *Resolver::GetCMISuffix ()
  55. {
  56. return "cmi";
  57. }
  58. std::string Resolver::GetCMIName (std::string const &module)
  59. {
  60. std::string result;
  61. result.reserve (module.size () + 8);
  62. bool is_header = false;
  63. bool is_abs = false;
  64. if (IsAbsPath (module.c_str ()))
  65. is_header = is_abs = true;
  66. else if (module.front () == '.' && IsDirSep (module.c_str ()[1]))
  67. is_header = true;
  68. if (is_abs)
  69. {
  70. result.push_back ('.');
  71. result.append (module);
  72. }
  73. else
  74. result = std::move (module);
  75. if (is_header)
  76. {
  77. if (!is_abs)
  78. result[0] = DOT_REPLACE;
  79. /* Map .. to DOT_REPLACE, DOT_REPLACE. */
  80. for (size_t ix = 1; ; ix++)
  81. {
  82. ix = result.find ('.', ix);
  83. if (ix == result.npos)
  84. break;
  85. if (ix + 2 > result.size ())
  86. break;
  87. if (result[ix + 1] != '.')
  88. continue;
  89. if (!IsDirSep (result[ix - 1]))
  90. continue;
  91. if (!IsDirSep (result[ix + 2]))
  92. continue;
  93. result[ix] = DOT_REPLACE;
  94. result[ix + 1] = DOT_REPLACE;
  95. }
  96. }
  97. else if (COLON_REPLACE != ':')
  98. {
  99. // There can only be one colon in a module name
  100. auto colon = result.find (':');
  101. if (colon != result.npos)
  102. result[colon] = COLON_REPLACE;
  103. }
  104. if (char const *suffix = GetCMISuffix ())
  105. {
  106. result.push_back ('.');
  107. result.append (suffix);
  108. }
  109. return result;
  110. }
  111. void Resolver::WaitUntilReady (Server *)
  112. {
  113. }
  114. Resolver *Resolver::ConnectRequest (Server *s, unsigned version,
  115. std::string &, std::string &)
  116. {
  117. if (version > Version)
  118. s->ErrorResponse ("version mismatch");
  119. else
  120. s->ConnectResponse ("default");
  121. return this;
  122. }
  123. int Resolver::ModuleRepoRequest (Server *s)
  124. {
  125. s->PathnameResponse (REPO_DIR);
  126. return 0;
  127. }
  128. // Deprecated resolver functions
  129. int Resolver::ModuleExportRequest (Server *s, Flags, std::string &module)
  130. {
  131. auto cmi = GetCMIName (module);
  132. s->PathnameResponse (cmi);
  133. return 0;
  134. }
  135. int Resolver::ModuleImportRequest (Server *s, Flags, std::string &module)
  136. {
  137. auto cmi = GetCMIName (module);
  138. s->PathnameResponse (cmi);
  139. return 0;
  140. }
  141. int Resolver::ModuleCompiledRequest (Server *s, Flags, std::string &)
  142. {
  143. s->OKResponse ();
  144. return 0;
  145. }
  146. int Resolver::IncludeTranslateRequest (Server *s, Flags, std::string &include)
  147. {
  148. bool xlate = false;
  149. // This is not the most efficient
  150. auto cmi = GetCMIName (include);
  151. struct stat statbuf;
  152. #if HAVE_FSTATAT
  153. int fd_dir = open (REPO_DIR, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
  154. if (fd_dir >= 0
  155. && fstatat (fd_dir, cmi.c_str (), &statbuf, 0) == 0
  156. && S_ISREG (statbuf.st_mode))
  157. // Sadly can't easily check if this process has read access,
  158. // except by trying to open it.
  159. xlate = true;
  160. if (fd_dir >= 0)
  161. close (fd_dir);
  162. #else
  163. std::string append = REPO_DIR;
  164. append.push_back (DIR_SEPARATOR);
  165. append.append (cmi);
  166. if (stat (append.c_str (), &statbuf) == 0
  167. || S_ISREG (statbuf.st_mode))
  168. xlate = true;
  169. #endif
  170. if (xlate)
  171. s->PathnameResponse (cmi);
  172. else
  173. s->BoolResponse (false);
  174. return 0;
  175. }
  176. void Resolver::ErrorResponse (Server *server, std::string &&msg)
  177. {
  178. server->ErrorResponse (msg);
  179. }
  180. }