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