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__ 28inline bool IsDirSep (char c) 29{ 30 return c == '/' || c == '\\'; 31} 32inline 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 41inline bool IsDirSep (char c) 42{ 43 return c == '/'; 44} 45inline bool IsAbsPath (char const *str) 46{ 47 return IsDirSep (str[0]); 48} 49#endif 50 51constexpr char DIR_SEPARATOR = '/'; 52 53constexpr char DOT_REPLACE = ','; // Replace . directories 54constexpr char COLON_REPLACE = '-'; // Replace : (partition char) 55constexpr char const REPO_DIR[] = "cmi.cache"; 56 57namespace Cody { 58 59Resolver::~Resolver () 60{ 61} 62 63char const *Resolver::GetCMISuffix () 64{ 65 return "cmi"; 66} 67 68std::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 129void Resolver::WaitUntilReady (Server *) 130{ 131} 132 133Resolver *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 144int Resolver::ModuleRepoRequest (Server *s) 145{ 146 s->PathnameResponse (REPO_DIR); 147 return 0; 148} 149 150// Deprecated resolver functions 151int Resolver::ModuleExportRequest (Server *s, Flags, std::string &module) 152{ 153 auto cmi = GetCMIName (module); 154 s->PathnameResponse (cmi); 155 return 0; 156} 157 158int Resolver::ModuleImportRequest (Server *s, Flags, std::string &module) 159{ 160 auto cmi = GetCMIName (module); 161 s->PathnameResponse (cmi); 162 return 0; 163} 164 165int Resolver::ModuleCompiledRequest (Server *s, Flags, std::string &) 166{ 167 s->OKResponse (); 168 return 0; 169} 170 171int 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 206void Resolver::ErrorResponse (Server *server, std::string &&msg) 207{ 208 server->ErrorResponse (msg); 209} 210 211} 212