xref: /netbsd/external/gpl3/gcc/dist/libcody/client.cc (revision f0fbc68b)
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 
Client()34 Client::Client ()
35 {
36   fd.from = fd.to = -1;
37 }
38 
Client(Client && src)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 
~Client()55 Client::~Client ()
56 {
57 }
58 
operator =(Client && src)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 
CommunicateWithServer()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 
CommunicationError(int err)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 
ProcessResponse(std::vector<std::string> & words,unsigned code,bool isLast)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 		   words.size () == 2 ? words[1]: u8"malformed error response");
126 
127   if (isLast && !read.IsAtEnd ())
128     return Packet (Client::PC_ERROR,
129 		   std::string (u8"unexpected extra response"));
130 
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 ())
135     {
136       std::string msg {u8"malformed response '"};
137 
138       read.LexedLine (msg);
139       msg.append (u8"'");
140       result.GetString () = std::move (msg);
141     }
142   else if (result.GetCode () == Client::PC_CONNECT)
143     is_connected = true;
144 
145   return result;
146 }
147 
MaybeRequest(unsigned code)148 Packet Client::MaybeRequest (unsigned code)
149 {
150   if (IsCorked ())
151     {
152       corked.push_back (code);
153       return Packet (PC_CORKED);
154     }
155 
156   if (int err = CommunicateWithServer ())
157     return CommunicationError (err);
158 
159   std::vector<std::string> words;
160   return ProcessResponse(words, code, true);
161 }
162 
Cork()163 void Client::Cork ()
164 {
165   if (corked.empty ())
166     corked.push_back (-1);
167 }
168 
Uncork()169 std::vector<Packet> Client::Uncork ()
170 {
171   std::vector<Packet> result;
172 
173   if (corked.size () > 1)
174     {
175       if (int err = CommunicateWithServer ())
176 	result.emplace_back (CommunicationError (err));
177       else
178 	{
179 	  std::vector<std::string> words;
180 	  for (auto iter = corked.begin () + 1; iter != corked.end ();)
181 	    {
182 	      char code = *iter;
183 	      ++iter;
184 	      result.emplace_back (ProcessResponse (words, code,
185 						    iter == corked.end ()));
186 	    }
187 	}
188     }
189 
190   corked.clear ();
191 
192   return result;
193 }
194 
195 // Now the individual message handlers
196 
197 // HELLO $vernum $agent $ident
Connect(char const * agent,char const * ident,size_t alen,size_t ilen)198 Packet Client::Connect (char const *agent, char const *ident,
199 			  size_t alen, size_t ilen)
200 {
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 ();
207 
208   return MaybeRequest (Detail::RC_CONNECT);
209 }
210 
211 // HELLO $version $agent [$flags]
ConnectResponse(std::vector<std::string> & words)212 Packet ConnectResponse (std::vector<std::string> &words)
213 {
214   if (words[0] == u8"HELLO" && (words.size () == 3 || words.size () == 4))
215     {
216       char *eptr;
217       unsigned long val = strtoul (words[1].c_str (), &eptr, 10);
218 
219       unsigned version = unsigned (val);
220       if (*eptr || version != val || version < Version)
221 	return Packet (Client::PC_ERROR, u8"incompatible version");
222       else
223 	{
224 	  unsigned flags = 0;
225 	  if (words.size () == 4)
226 	    {
227 	      val = strtoul (words[3].c_str (), &eptr, 10);
228 	      flags = unsigned (val);
229 	    }
230 	  return Packet (Client::PC_CONNECT, flags);
231 	}
232     }
233 
234   return Packet (Client::PC_ERROR, u8"");
235 }
236 
237 // MODULE-REPO
ModuleRepo()238 Packet Client::ModuleRepo ()
239 {
240   write.BeginLine ();
241   write.AppendWord (u8"MODULE-REPO");
242   write.EndLine ();
243 
244   return MaybeRequest (Detail::RC_MODULE_REPO);
245 }
246 
247 // PATHNAME $dir | ERROR
PathnameResponse(std::vector<std::string> & words)248 Packet PathnameResponse (std::vector<std::string> &words)
249 {
250   if (words[0] == u8"PATHNAME" && words.size () == 2)
251     return Packet (Client::PC_PATHNAME, std::move (words[1]));
252 
253   return Packet (Client::PC_ERROR, u8"");
254 }
255 
256 // OK or ERROR
OKResponse(std::vector<std::string> & words)257 Packet OKResponse (std::vector<std::string> &words)
258 {
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]) : "");
264 }
265 
266 // MODULE-EXPORT $modulename [$flags]
ModuleExport(char const * module,Flags flags,size_t mlen)267 Packet Client::ModuleExport (char const *module, Flags flags, size_t mlen)
268 {
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 ();
275 
276   return MaybeRequest (Detail::RC_MODULE_EXPORT);
277 }
278 
279 // MODULE-IMPORT $modulename [$flags]
ModuleImport(char const * module,Flags flags,size_t mlen)280 Packet Client::ModuleImport (char const *module, Flags flags, size_t mlen)
281 {
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 ();
288 
289   return MaybeRequest (Detail::RC_MODULE_IMPORT);
290 }
291 
292 // MODULE-COMPILED $modulename [$flags]
ModuleCompiled(char const * module,Flags flags,size_t mlen)293 Packet Client::ModuleCompiled (char const *module, Flags flags, size_t mlen)
294 {
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 ();
301 
302   return MaybeRequest (Detail::RC_MODULE_COMPILED);
303 }
304 
305 // INCLUDE-TRANSLATE $includename [$flags]
IncludeTranslate(char const * include,Flags flags,size_t ilen)306 Packet Client::IncludeTranslate (char const *include, Flags flags, size_t ilen)
307 {
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 ();
314 
315   return MaybeRequest (Detail::RC_INCLUDE_TRANSLATE);
316 }
317 
318 // BOOL $knowntextualness
319 // PATHNAME $cmifile
IncludeTranslateResponse(std::vector<std::string> & words)320 Packet IncludeTranslateResponse (std::vector<std::string> &words)
321 {
322   if (words[0] == u8"BOOL" && words.size () == 2)
323     {
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"");
330     }
331   else
332     return PathnameResponse (words);
333 }
334 
335 }
336 
337