blob: e2fa069bb9335042156810d95573caeeabe76ccc [file] [log] [blame]
// CODYlib -*- mode:c++ -*-
// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
// License: Apache v2.0
// Cody
#include "internal.hh"
// C++
#include <tuple>
// C
#include <cerrno>
#include <cstdlib>
#include <cstring>
// Server code
namespace Cody {
// These do not need to be members
static Resolver *ConnectRequest (Server *, Resolver *,
std::vector<std::string> &words);
static int ModuleRepoRequest (Server *, Resolver *,
std::vector<std::string> &words);
static int ModuleExportRequest (Server *, Resolver *,
std::vector<std::string> &words);
static int ModuleImportRequest (Server *, Resolver *,
std::vector<std::string> &words);
static int ModuleCompiledRequest (Server *, Resolver *,
std::vector<std::string> &words);
static int IncludeTranslateRequest (Server *, Resolver *,
std::vector<std::string> &words);
namespace {
using RequestFn = int (Server *, Resolver *, std::vector<std::string> &);
using RequestPair = std::tuple<char const *, RequestFn *>;
static RequestPair
const requestTable[Detail::RC_HWM] =
{
// Same order as enum RequestCode
RequestPair {u8"HELLO", nullptr},
RequestPair {u8"MODULE-REPO", ModuleRepoRequest},
RequestPair {u8"MODULE-EXPORT", ModuleExportRequest},
RequestPair {u8"MODULE-IMPORT", ModuleImportRequest},
RequestPair {u8"MODULE-COMPILED", ModuleCompiledRequest},
RequestPair {u8"INCLUDE-TRANSLATE", IncludeTranslateRequest},
};
}
Server::Server (Resolver *r)
: resolver (r), direction (READING)
{
PrepareToRead ();
}
Server::Server (Server &&src)
: write (std::move (src.write)),
read (std::move (src.read)),
resolver (src.resolver),
is_connected (src.is_connected),
direction (src.direction)
{
fd.from = src.fd.from;
fd.to = src.fd.to;
}
Server::~Server ()
{
}
Server &Server::operator= (Server &&src)
{
write = std::move (src.write);
read = std::move (src.read);
resolver = src.resolver;
is_connected = src.is_connected;
direction = src.direction;
fd.from = src.fd.from;
fd.to = src.fd.to;
return *this;
}
void Server::DirectProcess (Detail::MessageBuffer &from,
Detail::MessageBuffer &to)
{
read.PrepareToRead ();
std::swap (read, from);
ProcessRequests ();
resolver->WaitUntilReady (this);
write.PrepareToWrite ();
std::swap (to, write);
}
void Server::ProcessRequests (void)
{
std::vector<std::string> words;
direction = PROCESSING;
while (!read.IsAtEnd ())
{
int err = 0;
unsigned ix = Detail::RC_HWM;
if (!read.Lex (words))
{
Assert (!words.empty ());
while (ix--)
{
if (words[0] != std::get<0> (requestTable[ix]))
continue; // not this one
if (ix == Detail::RC_CONNECT)
{
// CONNECT
if (IsConnected ())
err = -1;
else if (auto *r = ConnectRequest (this, resolver, words))
resolver = r;
else
err = -1;
}
else
{
if (!IsConnected ())
err = -1;
else if (int res = (std::get<1> (requestTable[ix])
(this, resolver, words)))
err = res;
}
break;
}
}
if (err || ix >= Detail::RC_HWM)
{
// Some kind of error
std::string msg;
if (err > 0)
msg = u8"error processing '";
else if (ix >= Detail::RC_HWM)
msg = u8"unrecognized '";
else if (IsConnected () && ix == Detail::RC_CONNECT)
msg = u8"already connected '";
else if (!IsConnected () && ix != Detail::RC_CONNECT)
msg = u8"not connected '";
else
msg = u8"malformed '";
read.LexedLine (msg);
msg.append (u8"'");
if (err > 0)
{
msg.append (u8" ");
msg.append (strerror (err));
}
resolver->ErrorResponse (this, std::move (msg));
}
}
}
// Return numeric value of STR as an unsigned. Returns ~0u on error
// (so that value is not representable).
static unsigned ParseUnsigned (std::string &str)
{
char *eptr;
unsigned long val = strtoul (str.c_str (), &eptr, 10);
if (*eptr || unsigned (val) != val)
return ~0u;
return unsigned (val);
}
Resolver *ConnectRequest (Server *s, Resolver *r,
std::vector<std::string> &words)
{
if (words.size () < 3 || words.size () > 4)
return nullptr;
if (words.size () == 3)
words.emplace_back (u8"");
unsigned version = ParseUnsigned (words[1]);
if (version == ~0u)
return nullptr;
return r->ConnectRequest (s, version, words[2], words[3]);
}
int ModuleRepoRequest (Server *s, Resolver *r,std::vector<std::string> &words)
{
if (words.size () != 1)
return -1;
return r->ModuleRepoRequest (s);
}
int ModuleExportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
{
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
return -1;
Flags flags = Flags::None;
if (words.size () == 3)
{
unsigned val = ParseUnsigned (words[2]);
if (val == ~0u)
return -1;
flags = Flags (val);
}
return r->ModuleExportRequest (s, flags, words[1]);
}
int ModuleImportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
{
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
return -1;
Flags flags = Flags::None;
if (words.size () == 3)
{
unsigned val = ParseUnsigned (words[2]);
if (val == ~0u)
return -1;
flags = Flags (val);
}
return r->ModuleImportRequest (s, flags, words[1]);
}
int ModuleCompiledRequest (Server *s, Resolver *r,
std::vector<std::string> &words)
{
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
return -1;
Flags flags = Flags::None;
if (words.size () == 3)
{
unsigned val = ParseUnsigned (words[2]);
if (val == ~0u)
return -1;
flags = Flags (val);
}
return r->ModuleCompiledRequest (s, flags, words[1]);
}
int IncludeTranslateRequest (Server *s, Resolver *r,
std::vector<std::string> &words)
{
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
return -1;
Flags flags = Flags::None;
if (words.size () == 3)
{
unsigned val = ParseUnsigned (words[2]);
if (val == ~0u)
return -1;
flags = Flags (val);
}
return r->IncludeTranslateRequest (s, flags, words[1]);
}
void Server::ErrorResponse (char const *error, size_t elen)
{
write.BeginLine ();
write.AppendWord (u8"ERROR");
write.AppendWord (error, true, elen);
write.EndLine ();
}
void Server::OKResponse ()
{
write.BeginLine ();
write.AppendWord (u8"OK");
write.EndLine ();
}
void Server::ConnectResponse (char const *agent, size_t alen)
{
is_connected = true;
write.BeginLine ();
write.AppendWord (u8"HELLO");
write.AppendInteger (Version);
write.AppendWord (agent, true, alen);
write.EndLine ();
}
void Server::PathnameResponse (char const *cmi, size_t clen)
{
write.BeginLine ();
write.AppendWord (u8"PATHNAME");
write.AppendWord (cmi, true, clen);
write.EndLine ();
}
void Server::BoolResponse (bool truthiness)
{
write.BeginLine ();
write.AppendWord (u8"BOOL");
write.AppendWord (truthiness ? u8"TRUE" : u8"FALSE");
write.EndLine ();
}
}