extend.texi: Mark builtin arguments with @var{...}
[official-gcc.git] / libcody / client.cc
blobae69d190cb77fca26550c141a99cf45c78ac0045
1 // CODYlib -*- mode:c++ -*-
2 // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
3 // License: Apache v2.0
5 // Cody
6 #include "internal.hh"
7 // C
8 #include <cerrno>
9 #include <cstdlib>
10 #include <cstring>
12 // Client code
14 namespace Cody {
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);
22 // Must be consistently ordered with the RequestCode enum
23 static Packet (*const responseTable[Detail::RC_HWM])
24 (std::vector<std::string> &) =
26 &ConnectResponse,
27 &PathnameResponse,
28 &PathnameResponse,
29 &PathnameResponse,
30 &OKResponse,
31 &IncludeTranslateResponse,
34 Client::Client ()
36 fd.from = fd.to = -1;
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)
46 if (is_direct)
47 server = src.server;
48 else
50 fd.from = src.fd.from;
51 fd.to = src.fd.to;
55 Client::~Client ()
59 Client &Client::operator= (Client &&src)
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
70 fd.from = src.fd.from;
71 fd.to = src.fd.to;
74 return *this;
77 int Client::CommunicateWithServer ()
79 write.PrepareToWrite ();
80 read.PrepareToRead ();
81 if (IsDirect ())
82 server->DirectProcess (write, read);
83 else
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;
95 return 0;
98 static Packet CommunicationError (int err)
100 std::string e {u8"communication error:"};
101 e.append (strerror (err));
103 return Packet (Client::PC_ERROR, std::move (e));
106 Packet Client::ProcessResponse (std::vector<std::string> &words,
107 unsigned code, bool isLast)
109 if (int e = read.Lex (words))
111 if (e == EINVAL)
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));
118 else
119 return Packet (Client::PC_ERROR, u8"missing response");
122 Assert (!words.empty ());
123 if (words[0] == u8"ERROR")
124 return Packet (Client::PC_ERROR,
125 words.size () == 2 ? words[1]: u8"malformed error response");
127 if (isLast && !read.IsAtEnd ())
128 return Packet (Client::PC_ERROR,
129 std::string (u8"unexpected extra response"));
131 Assert (code < Detail::RC_HWM);
132 Packet result (responseTable[code] (words));
133 result.SetRequest (code);
134 if (result.GetCode () == Client::PC_ERROR && result.GetString ().empty ())
136 std::string msg {u8"malformed response '"};
138 read.LexedLine (msg);
139 msg.append (u8"'");
140 result.GetString () = std::move (msg);
142 else if (result.GetCode () == Client::PC_CONNECT)
143 is_connected = true;
145 return result;
148 Packet Client::MaybeRequest (unsigned code)
150 if (IsCorked ())
152 corked.push_back (code);
153 return Packet (PC_CORKED);
156 if (int err = CommunicateWithServer ())
157 return CommunicationError (err);
159 std::vector<std::string> words;
160 return ProcessResponse(words, code, true);
163 void Client::Cork ()
165 if (corked.empty ())
166 corked.push_back (-1);
169 std::vector<Packet> Client::Uncork ()
171 std::vector<Packet> result;
173 if (corked.size () > 1)
175 if (int err = CommunicateWithServer ())
176 result.emplace_back (CommunicationError (err));
177 else
179 std::vector<std::string> words;
180 for (auto iter = corked.begin () + 1; iter != corked.end ();)
182 char code = *iter;
183 ++iter;
184 result.emplace_back (ProcessResponse (words, code,
185 iter == corked.end ()));
190 corked.clear ();
192 return result;
195 // Now the individual message handlers
197 // HELLO $vernum $agent $ident
198 Packet Client::Connect (char const *agent, char const *ident,
199 size_t alen, size_t ilen)
201 write.BeginLine ();
202 write.AppendWord (u8"HELLO");
203 write.AppendInteger (Version);
204 write.AppendWord (agent, true, alen);
205 write.AppendWord (ident, true, ilen);
206 write.EndLine ();
208 return MaybeRequest (Detail::RC_CONNECT);
211 // HELLO $version $agent [$flags]
212 Packet ConnectResponse (std::vector<std::string> &words)
214 if (words[0] == u8"HELLO" && (words.size () == 3 || words.size () == 4))
216 char *eptr;
217 unsigned long val = strtoul (words[1].c_str (), &eptr, 10);
219 unsigned version = unsigned (val);
220 if (*eptr || version != val || version < Version)
221 return Packet (Client::PC_ERROR, u8"incompatible version");
222 else
224 unsigned flags = 0;
225 if (words.size () == 4)
227 val = strtoul (words[3].c_str (), &eptr, 10);
228 flags = unsigned (val);
230 return Packet (Client::PC_CONNECT, flags);
234 return Packet (Client::PC_ERROR, u8"");
237 // MODULE-REPO
238 Packet Client::ModuleRepo ()
240 write.BeginLine ();
241 write.AppendWord (u8"MODULE-REPO");
242 write.EndLine ();
244 return MaybeRequest (Detail::RC_MODULE_REPO);
247 // PATHNAME $dir | ERROR
248 Packet PathnameResponse (std::vector<std::string> &words)
250 if (words[0] == u8"PATHNAME" && words.size () == 2)
251 return Packet (Client::PC_PATHNAME, std::move (words[1]));
253 return Packet (Client::PC_ERROR, u8"");
256 // OK or ERROR
257 Packet OKResponse (std::vector<std::string> &words)
259 if (words[0] == u8"OK")
260 return Packet (Client::PC_OK);
261 else
262 return Packet (Client::PC_ERROR,
263 words.size () == 2 ? std::move (words[1]) : "");
266 // MODULE-EXPORT $modulename [$flags]
267 Packet Client::ModuleExport (char const *module, Flags flags, size_t mlen)
269 write.BeginLine ();
270 write.AppendWord (u8"MODULE-EXPORT");
271 write.AppendWord (module, true, mlen);
272 if (flags != Flags::None)
273 write.AppendInteger (unsigned (flags));
274 write.EndLine ();
276 return MaybeRequest (Detail::RC_MODULE_EXPORT);
279 // MODULE-IMPORT $modulename [$flags]
280 Packet Client::ModuleImport (char const *module, Flags flags, size_t mlen)
282 write.BeginLine ();
283 write.AppendWord (u8"MODULE-IMPORT");
284 write.AppendWord (module, true, mlen);
285 if (flags != Flags::None)
286 write.AppendInteger (unsigned (flags));
287 write.EndLine ();
289 return MaybeRequest (Detail::RC_MODULE_IMPORT);
292 // MODULE-COMPILED $modulename [$flags]
293 Packet Client::ModuleCompiled (char const *module, Flags flags, size_t mlen)
295 write.BeginLine ();
296 write.AppendWord (u8"MODULE-COMPILED");
297 write.AppendWord (module, true, mlen);
298 if (flags != Flags::None)
299 write.AppendInteger (unsigned (flags));
300 write.EndLine ();
302 return MaybeRequest (Detail::RC_MODULE_COMPILED);
305 // INCLUDE-TRANSLATE $includename [$flags]
306 Packet Client::IncludeTranslate (char const *include, Flags flags, size_t ilen)
308 write.BeginLine ();
309 write.AppendWord (u8"INCLUDE-TRANSLATE");
310 write.AppendWord (include, true, ilen);
311 if (flags != Flags::None)
312 write.AppendInteger (unsigned (flags));
313 write.EndLine ();
315 return MaybeRequest (Detail::RC_INCLUDE_TRANSLATE);
318 // BOOL $knowntextualness
319 // PATHNAME $cmifile
320 Packet IncludeTranslateResponse (std::vector<std::string> &words)
322 if (words[0] == u8"BOOL" && words.size () == 2)
324 if (words[1] == u8"FALSE")
325 return Packet (Client::PC_BOOL, 0);
326 else if (words[1] == u8"TRUE")
327 return Packet (Client::PC_BOOL, 1);
328 else
329 return Packet (Client::PC_ERROR, u8"");
331 else
332 return PathnameResponse (words);