testsuite: Fix up pr56719.c testcase [PR98489]
[gcc.git] / libcody / client.cc
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 // C
8 #include <cerrno>
9 #include <cstdlib>
10 #include <cstring>
11
12 // Client code
13
14 namespace Cody {
15
16 // These do not need to be members
17 static Packet ConnectResponse (std::vector<std::string> &words);
18 static Packet PathnameResponse (std::vector<std::string> &words);
19 static Packet OKResponse (std::vector<std::string> &words);
20 static Packet IncludeTranslateResponse (std::vector<std::string> &words);
21
22 // Must be consistently ordered with the RequestCode enum
23 static Packet (*const responseTable[Detail::RC_HWM])
24 (std::vector<std::string> &) =
25 {
26 &ConnectResponse,
27 &PathnameResponse,
28 &PathnameResponse,
29 &PathnameResponse,
30 &OKResponse,
31 &IncludeTranslateResponse,
32 };
33
34 Client::Client ()
35 {
36 fd.from = fd.to = -1;
37 }
38
39 Client::Client (Client &&src)
40 : write (std::move (src.write)),
41 read (std::move (src.read)),
42 corked (std::move (src.corked)),
43 is_direct (src.is_direct),
44 is_connected (src.is_connected)
45 {
46 if (is_direct)
47 server = src.server;
48 else
49 {
50 fd.from = src.fd.from;
51 fd.to = src.fd.to;
52 }
53 }
54
55 Client::~Client ()
56 {
57 }
58
59 Client &Client::operator= (Client &&src)
60 {
61 write = std::move (src.write);
62 read = std::move (src.read);
63 corked = std::move (src.corked);
64 is_direct = src.is_direct;
65 is_connected = src.is_connected;
66 if (is_direct)
67 server = src.server;
68 else
69 {
70 fd.from = src.fd.from;
71 fd.to = src.fd.to;
72 }
73
74 return *this;
75 }
76
77 int Client::CommunicateWithServer ()
78 {
79 write.PrepareToWrite ();
80 read.PrepareToRead ();
81 if (IsDirect ())
82 server->DirectProcess (write, read);
83 else
84 {
85 // Write the write buffer
86 while (int e = write.Write (fd.to))
87 if (e != EAGAIN && e != EINTR)
88 return e;
89 // Read the read buffer
90 while (int e = read.Read (fd.from))
91 if (e != EAGAIN && e != EINTR)
92 return e;
93 }
94
95 return 0;
96 }
97
98 static Packet CommunicationError (int err)
99 {
100 std::string e {u8"communication error:"};
101 e.append (strerror (err));
102
103 return Packet (Client::PC_ERROR, std::move (e));
104 }
105
106 Packet Client::ProcessResponse (std::vector<std::string> &words,
107 unsigned code, bool isLast)
108 {
109 if (int e = read.Lex (words))
110 {
111 if (e == EINVAL)
112 {
113 std::string msg (u8"malformed string '");
114 msg.append (words[0]);
115 msg.append (u8"'");
116 return Packet (Client::PC_ERROR, std::move (msg));
117 }
118 else
119 return Packet (Client::PC_ERROR, u8"missing response");
120 }
121
122 Assert (!words.empty ());
123 if (words[0] == u8"ERROR")
124 return Packet (Client::PC_ERROR,
125 std::move (words.size () == 2 ? words[1]
126 : u8"malformed error response"));
127
128 if (isLast && !read.IsAtEnd ())
129 return Packet (Client::PC_ERROR,
130 std::string (u8"unexpected extra response"));
131
132 Assert (code < Detail::RC_HWM);
133 Packet result (responseTable[code] (words));
134 result.SetRequest (code);
135 if (result.GetCode () == Client::PC_ERROR && result.GetString ().empty ())
136 {
137 std::string msg {u8"malformed response '"};
138
139 read.LexedLine (msg);
140 msg.append (u8"'");
141 result.GetString () = std::move (msg);
142 }
143 else if (result.GetCode () == Client::PC_CONNECT)
144 is_connected = true;
145
146 return result;
147 }
148
149 Packet Client::MaybeRequest (unsigned code)
150 {
151 if (IsCorked ())
152 {
153 corked.push_back (code);
154 return Packet (PC_CORKED);
155 }
156
157 if (int err = CommunicateWithServer ())
158 return CommunicationError (err);
159
160 std::vector<std::string> words;
161 return ProcessResponse(words, code, true);
162 }
163
164 void Client::Cork ()
165 {
166 if (corked.empty ())
167 corked.push_back (-1);
168 }
169
170 std::vector<Packet> Client::Uncork ()
171 {
172 std::vector<Packet> result;
173
174 if (corked.size () > 1)
175 {
176 if (int err = CommunicateWithServer ())
177 result.emplace_back (CommunicationError (err));
178 else
179 {
180 std::vector<std::string> words;
181 for (auto iter = corked.begin () + 1; iter != corked.end ();)
182 {
183 char code = *iter;
184 ++iter;
185 result.emplace_back (ProcessResponse (words, code,
186 iter == corked.end ()));
187 }
188 }
189 }
190
191 corked.clear ();
192
193 return result;
194 }
195
196 // Now the individual message handlers
197
198 // HELLO $vernum $agent $ident
199 Packet Client::Connect (char const *agent, char const *ident,
200 size_t alen, size_t ilen)
201 {
202 write.BeginLine ();
203 write.AppendWord (u8"HELLO");
204 write.AppendInteger (Version);
205 write.AppendWord (agent, true, alen);
206 write.AppendWord (ident, true, ilen);
207 write.EndLine ();
208
209 return MaybeRequest (Detail::RC_CONNECT);
210 }
211
212 // HELLO $version $agent [$flags]
213 Packet ConnectResponse (std::vector<std::string> &words)
214 {
215 if (words[0] == u8"HELLO" && (words.size () == 3 || words.size () == 4))
216 {
217 char *eptr;
218 unsigned long val = strtoul (words[1].c_str (), &eptr, 10);
219
220 unsigned version = unsigned (val);
221 if (*eptr || version != val || version < Version)
222 return Packet (Client::PC_ERROR, u8"incompatible version");
223 else
224 {
225 unsigned flags = 0;
226 if (words.size () == 4)
227 {
228 val = strtoul (words[3].c_str (), &eptr, 10);
229 flags = unsigned (val);
230 }
231 return Packet (Client::PC_CONNECT, flags);
232 }
233 }
234
235 return Packet (Client::PC_ERROR, u8"");
236 }
237
238 // MODULE-REPO
239 Packet Client::ModuleRepo ()
240 {
241 write.BeginLine ();
242 write.AppendWord (u8"MODULE-REPO");
243 write.EndLine ();
244
245 return MaybeRequest (Detail::RC_MODULE_REPO);
246 }
247
248 // PATHNAME $dir | ERROR
249 Packet PathnameResponse (std::vector<std::string> &words)
250 {
251 if (words[0] == u8"PATHNAME" && words.size () == 2)
252 return Packet (Client::PC_PATHNAME, std::move (words[1]));
253
254 return Packet (Client::PC_ERROR, u8"");
255 }
256
257 // OK or ERROR
258 Packet OKResponse (std::vector<std::string> &words)
259 {
260 if (words[0] == u8"OK")
261 return Packet (Client::PC_OK);
262 else
263 return Packet (Client::PC_ERROR,
264 words.size () == 2 ? std::move (words[1]) : "");
265 }
266
267 // MODULE-EXPORT $modulename [$flags]
268 Packet Client::ModuleExport (char const *module, Flags flags, size_t mlen)
269 {
270 write.BeginLine ();
271 write.AppendWord (u8"MODULE-EXPORT");
272 write.AppendWord (module, true, mlen);
273 if (flags != Flags::None)
274 write.AppendInteger (unsigned (flags));
275 write.EndLine ();
276
277 return MaybeRequest (Detail::RC_MODULE_EXPORT);
278 }
279
280 // MODULE-IMPORT $modulename [$flags]
281 Packet Client::ModuleImport (char const *module, Flags flags, size_t mlen)
282 {
283 write.BeginLine ();
284 write.AppendWord (u8"MODULE-IMPORT");
285 write.AppendWord (module, true, mlen);
286 if (flags != Flags::None)
287 write.AppendInteger (unsigned (flags));
288 write.EndLine ();
289
290 return MaybeRequest (Detail::RC_MODULE_IMPORT);
291 }
292
293 // MODULE-COMPILED $modulename [$flags]
294 Packet Client::ModuleCompiled (char const *module, Flags flags, size_t mlen)
295 {
296 write.BeginLine ();
297 write.AppendWord (u8"MODULE-COMPILED");
298 write.AppendWord (module, true, mlen);
299 if (flags != Flags::None)
300 write.AppendInteger (unsigned (flags));
301 write.EndLine ();
302
303 return MaybeRequest (Detail::RC_MODULE_COMPILED);
304 }
305
306 // INCLUDE-TRANSLATE $includename [$flags]
307 Packet Client::IncludeTranslate (char const *include, Flags flags, size_t ilen)
308 {
309 write.BeginLine ();
310 write.AppendWord (u8"INCLUDE-TRANSLATE");
311 write.AppendWord (include, true, ilen);
312 if (flags != Flags::None)
313 write.AppendInteger (unsigned (flags));
314 write.EndLine ();
315
316 return MaybeRequest (Detail::RC_INCLUDE_TRANSLATE);
317 }
318
319 // BOOL $knowntextualness
320 // PATHNAME $cmifile
321 Packet IncludeTranslateResponse (std::vector<std::string> &words)
322 {
323 if (words[0] == u8"BOOL" && words.size () == 2)
324 {
325 if (words[1] == u8"FALSE")
326 return Packet (Client::PC_BOOL, 0);
327 else if (words[1] == u8"TRUE")
328 return Packet (Client::PC_BOOL, 1);
329 else
330 return Packet (Client::PC_ERROR, u8"");
331 }
332 else
333 return PathnameResponse (words);
334 }
335
336 }
337