blob: 034fd63b9a880846f33ca04daf133ac01178d6ba [file] [log] [blame]
// CODYlib -*- mode:c++ -*-
// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
// License: Apache v2.0
// Cody
#include "internal.hh"
// OS
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#if ((defined (__unix__) \
&& defined _POSIX_C_SOURCE \
&& (_POSIX_C_SOURCE - 0) >= 200809L) \
|| (defined (__Apple__) \
&& defined (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) \
&& __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000))
// Autoconf test?
#define HAVE_FSTATAT 1
#else
#define HAVE_FSTATAT 0
#endif
// Resolver code
#if __windows__
inline bool IsDirSep (char c)
{
return c == '/' || c == '\\';
}
inline bool IsAbsPath (char const *str)
{
// IIRC windows has the concept of per-drive current directories,
// which make drive-using paths confusing. Let's not get into that.
return IsDirSep (str)
|| (((str[0] >= 'A' && str[0] <= 'Z')
|| (str[0] >= 'a' && str[0] <= 'z'))&& str[1] == ':');
}
#else
inline bool IsDirSep (char c)
{
return c == '/';
}
inline bool IsAbsPath (char const *str)
{
return IsDirSep (str[0]);
}
#endif
constexpr char DIR_SEPARATOR = '/';
constexpr char DOT_REPLACE = ','; // Replace . directories
constexpr char COLON_REPLACE = '-'; // Replace : (partition char)
constexpr char const REPO_DIR[] = "cmi.cache";
namespace Cody {
Resolver::~Resolver ()
{
}
char const *Resolver::GetCMISuffix ()
{
return "cmi";
}
std::string Resolver::GetCMIName (std::string const &module)
{
std::string result;
result.reserve (module.size () + 8);
bool is_header = false;
bool is_abs = false;
if (IsAbsPath (module.c_str ()))
is_header = is_abs = true;
else if (module.front () == '.' && IsDirSep (module.c_str ()[1]))
is_header = true;
if (is_abs)
{
result.push_back ('.');
result.append (module);
}
else
result = std::move (module);
if (is_header)
{
if (!is_abs)
result[0] = DOT_REPLACE;
/* Map .. to DOT_REPLACE, DOT_REPLACE. */
for (size_t ix = 1; ; ix++)
{
ix = result.find ('.', ix);
if (ix == result.npos)
break;
if (ix + 2 > result.size ())
break;
if (result[ix + 1] != '.')
continue;
if (!IsDirSep (result[ix - 1]))
continue;
if (!IsDirSep (result[ix + 2]))
continue;
result[ix] = DOT_REPLACE;
result[ix + 1] = DOT_REPLACE;
}
}
else if (COLON_REPLACE != ':')
{
// There can only be one colon in a module name
auto colon = result.find (':');
if (colon != result.npos)
result[colon] = COLON_REPLACE;
}
if (char const *suffix = GetCMISuffix ())
{
result.push_back ('.');
result.append (suffix);
}
return result;
}
void Resolver::WaitUntilReady (Server *)
{
}
Resolver *Resolver::ConnectRequest (Server *s, unsigned version,
std::string &, std::string &)
{
if (version > Version)
s->ErrorResponse ("version mismatch");
else
s->ConnectResponse ("default");
return this;
}
int Resolver::ModuleRepoRequest (Server *s)
{
s->PathnameResponse (REPO_DIR);
return 0;
}
// Deprecated resolver functions
int Resolver::ModuleExportRequest (Server *s, Flags, std::string &module)
{
auto cmi = GetCMIName (module);
s->PathnameResponse (cmi);
return 0;
}
int Resolver::ModuleImportRequest (Server *s, Flags, std::string &module)
{
auto cmi = GetCMIName (module);
s->PathnameResponse (cmi);
return 0;
}
int Resolver::ModuleCompiledRequest (Server *s, Flags, std::string &)
{
s->OKResponse ();
return 0;
}
int Resolver::IncludeTranslateRequest (Server *s, Flags, std::string &include)
{
bool xlate = false;
// This is not the most efficient
auto cmi = GetCMIName (include);
struct stat statbuf;
#if HAVE_FSTATAT
int fd_dir = open (REPO_DIR, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
if (fd_dir >= 0
&& fstatat (fd_dir, cmi.c_str (), &statbuf, 0) == 0
&& S_ISREG (statbuf.st_mode))
// Sadly can't easily check if this process has read access,
// except by trying to open it.
xlate = true;
if (fd_dir >= 0)
close (fd_dir);
#else
std::string append = REPO_DIR;
append.push_back (DIR_SEPARATOR);
append.append (cmi);
if (stat (append.c_str (), &statbuf) == 0
|| S_ISREG (statbuf.st_mode))
xlate = true;
#endif
if (xlate)
s->PathnameResponse (cmi);
else
s->BoolResponse (false);
return 0;
}
void Resolver::ErrorResponse (Server *server, std::string &&msg)
{
server->ErrorResponse (msg);
}
}