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