// CODYlib -*- mode:c++ -*- // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org // License: Apache v2.0 // Cody #include "internal.hh" // OS #include #include #include #include #if ((defined (__unix__) \ && defined _POSIX_C_SOURCE \ && (_POSIX_C_SOURCE - 0) >= 200809L) \ || (defined (__Apple__) \ && defined (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) \ && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000)) // Autoconf test? #define HAVE_FSTATAT 1 #else #define HAVE_FSTATAT 0 #endif // Resolver code #if __windows__ inline bool IsDirSep (char c) { return c == '/' || c == '\\'; } inline bool IsAbsPath (char const *str) { // IIRC windows has the concept of per-drive current directories, // which make drive-using paths confusing. Let's not get into that. return IsDirSep (str) || (((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z'))&& str[1] == ':'); } #else inline bool IsDirSep (char c) { return c == '/'; } inline bool IsAbsPath (char const *str) { return IsDirSep (str[0]); } #endif constexpr char DIR_SEPARATOR = '/'; constexpr char DOT_REPLACE = ','; // Replace . directories constexpr char COLON_REPLACE = '-'; // Replace : (partition char) constexpr char const REPO_DIR[] = "cmi.cache"; namespace Cody { Resolver::~Resolver () { } char const *Resolver::GetCMISuffix () { return "cmi"; } std::string Resolver::GetCMIName (std::string const &module) { std::string result; result.reserve (module.size () + 8); bool is_header = false; bool is_abs = false; if (IsAbsPath (module.c_str ())) is_header = is_abs = true; else if (module.front () == '.' && IsDirSep (module.c_str ()[1])) is_header = true; if (is_abs) { result.push_back ('.'); result.append (module); } else result = std::move (module); if (is_header) { if (!is_abs) result[0] = DOT_REPLACE; /* Map .. to DOT_REPLACE, DOT_REPLACE. */ for (size_t ix = 1; ; ix++) { ix = result.find ('.', ix); if (ix == result.npos) break; if (ix + 2 > result.size ()) break; if (result[ix + 1] != '.') continue; if (!IsDirSep (result[ix - 1])) continue; if (!IsDirSep (result[ix + 2])) continue; result[ix] = DOT_REPLACE; result[ix + 1] = DOT_REPLACE; } } else if (COLON_REPLACE != ':') { // There can only be one colon in a module name auto colon = result.find (':'); if (colon != result.npos) result[colon] = COLON_REPLACE; } if (char const *suffix = GetCMISuffix ()) { result.push_back ('.'); result.append (suffix); } return result; } void Resolver::WaitUntilReady (Server *) { } Resolver *Resolver::ConnectRequest (Server *s, unsigned version, std::string &, std::string &) { if (version > Version) s->ErrorResponse ("version mismatch"); else s->ConnectResponse ("default"); return this; } int Resolver::ModuleRepoRequest (Server *s) { s->PathnameResponse (REPO_DIR); return 0; } // Deprecated resolver functions int Resolver::ModuleExportRequest (Server *s, Flags, std::string &module) { auto cmi = GetCMIName (module); s->PathnameResponse (cmi); return 0; } int Resolver::ModuleImportRequest (Server *s, Flags, std::string &module) { auto cmi = GetCMIName (module); s->PathnameResponse (cmi); return 0; } int Resolver::ModuleCompiledRequest (Server *s, Flags, std::string &) { s->OKResponse (); return 0; } int Resolver::IncludeTranslateRequest (Server *s, Flags, std::string &include) { bool xlate = false; // This is not the most efficient auto cmi = GetCMIName (include); struct stat statbuf; #if HAVE_FSTATAT int fd_dir = open (REPO_DIR, O_RDONLY | O_CLOEXEC | O_DIRECTORY); if (fd_dir >= 0 && fstatat (fd_dir, cmi.c_str (), &statbuf, 0) == 0 && S_ISREG (statbuf.st_mode)) // Sadly can't easily check if this process has read access, // except by trying to open it. xlate = true; if (fd_dir >= 0) close (fd_dir); #else std::string append = REPO_DIR; append.push_back (DIR_SEPARATOR); append.append (cmi); if (stat (append.c_str (), &statbuf) == 0 || S_ISREG (statbuf.st_mode)) xlate = true; #endif if (xlate) s->PathnameResponse (cmi); else s->BoolResponse (false); return 0; } void Resolver::ErrorResponse (Server *server, std::string &&msg) { server->ErrorResponse (msg); } }