1139823Simp// CODYlib		-*- mode:c++ -*-
21541Srgrimes// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
31541Srgrimes// License: Apache v2.0
41541Srgrimes
51541Srgrimes// Cody
61541Srgrimes#include "internal.hh"
71541Srgrimes// OS
81541Srgrimes#include <fcntl.h>
91541Srgrimes#include <unistd.h>
101541Srgrimes#include <sys/stat.h>
111541Srgrimes#include <sys/types.h>
121541Srgrimes
131541Srgrimes#if ((defined (__unix__)						\
141541Srgrimes      && defined _POSIX_C_SOURCE					\
151541Srgrimes      && (_POSIX_C_SOURCE - 0) >= 200809L)				\
161541Srgrimes     || (defined (__Apple__)						\
171541Srgrimes	 && defined (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) 	\
181541Srgrimes	 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000))
191541Srgrimes// Autoconf test?
201541Srgrimes#define HAVE_FSTATAT 1
211541Srgrimes#else
221541Srgrimes#define HAVE_FSTATAT 0
231541Srgrimes#endif
241541Srgrimes
251541Srgrimes// Resolver code
261541Srgrimes
271541Srgrimes#if __windows__
281541Srgrimesinline bool IsDirSep (char c)
291541Srgrimes{
301541Srgrimes  return c == '/' || c == '\\';
311541Srgrimes}
3222521Sdysoninline bool IsAbsPath (char const *str)
331541Srgrimes{
341541Srgrimes  // IIRC windows has the concept of per-drive current directories,
3583651Speter  // which make drive-using paths confusing.  Let's not get into that.
3683651Speter  return IsDirSep (str)
3783651Speter    || (((str[0] >= 'A' && str[0] <= 'Z')
381541Srgrimes	 || (str[0] >= 'a' && str[0] <= 'z'))&& str[1] == ':');
391541Srgrimes}
4012274Sbde#else
411541Srgrimesinline bool IsDirSep (char c)
4219449Sdfr{
431541Srgrimes  return c == '/';
4415480Sbde}
451541Srgrimesinline bool IsAbsPath (char const *str)
4630354Sphk{
471541Srgrimes  return IsDirSep (str[0]);
481541Srgrimes}
4960041Sphk#endif
501541Srgrimes
511541Srgrimesconstexpr char DIR_SEPARATOR = '/';
521541Srgrimes
531541Srgrimesconstexpr char DOT_REPLACE = ','; // Replace . directories
541541Srgrimesconstexpr char COLON_REPLACE = '-'; // Replace : (partition char)
551541Srgrimesconstexpr char const REPO_DIR[] = "cmi.cache";
561541Srgrimes
5783651Speternamespace Cody {
5883651Speter
5975631SalfredResolver::~Resolver ()
6075631Salfred{
6183651Speter}
621541Srgrimes
631541Srgrimeschar const *Resolver::GetCMISuffix ()
641541Srgrimes{
65122698Salfred  return "cmi";
66122698Salfred}
67122698Salfred
689336Sdfrstd::string Resolver::GetCMIName (std::string const &module)
691541Srgrimes{
709336Sdfr  std::string result;
7183651Speter
7283651Speter  result.reserve (module.size () + 8);
7383651Speter  bool is_header = false;
7483651Speter  bool is_abs = false;
7583651Speter
761541Srgrimes  if (IsAbsPath (module.c_str ()))
77151897Srwatson    is_header = is_abs = true;
7830309Sphk  else if (module.front () == '.' && IsDirSep (module.c_str ()[1]))
7983651Speter    is_header = true;
801541Srgrimes
811541Srgrimes  if (is_abs)
8212457Sbde    {
8344112Sdfr      result.push_back ('.');
8444112Sdfr      result.append (module);
8589407Speter    }
8689407Speter  else
8789407Speter    result = std::move (module);
8889407Speter
8989407Speter  if (is_header)
90128111Speadar    {
9189407Speter      if (!is_abs)
9289324Speter	result[0] = DOT_REPLACE;
9389324Speter
9489324Speter      /* Map .. to DOT_REPLACE, DOT_REPLACE.  */
9589407Speter      for (size_t ix = 1; ; ix++)
9689407Speter	{
9789407Speter	  ix = result.find ('.', ix);
9889407Speter	  if (ix == result.npos)
9989407Speter	    break;
10089324Speter	  if (ix + 2 > result.size ())
10189407Speter	    break;
10289407Speter	  if (result[ix + 1] != '.')
10389407Speter	    continue;
10489407Speter	  if (!IsDirSep (result[ix - 1]))
10589407Speter	    continue;
10689407Speter	  if (!IsDirSep (result[ix + 2]))
10789407Speter	    continue;
10889407Speter	  result[ix] = DOT_REPLACE;
10989407Speter	  result[ix + 1] = DOT_REPLACE;
11089407Speter	}
11189407Speter    }
11289407Speter  else if (COLON_REPLACE != ':')
11389407Speter    {
11489407Speter      // There can only be one colon in a module name
11589407Speter      auto colon = result.find (':');
11689407Speter      if (colon != result.npos)
11789407Speter	result[colon] = COLON_REPLACE;
11889407Speter    }
11989407Speter
12089407Speter  if (char const *suffix = GetCMISuffix ())
12189407Speter    {
12289407Speter      result.push_back ('.');
12389407Speter      result.append (suffix);
12489407Speter    }
12589407Speter
12689407Speter  return result;
12789407Speter}
12889407Speter
12989407Spetervoid Resolver::WaitUntilReady (Server *)
13089407Speter{
13189407Speter}
13289407Speter
13389407SpeterResolver *Resolver::ConnectRequest (Server *s, unsigned version,
13489407Speter			       std::string &, std::string &)
13589407Speter{
13689407Speter  if (version > Version)
13789407Speter    s->ErrorResponse ("version mismatch");
13889407Speter  else
13989407Speter    s->ConnectResponse ("default");
14089407Speter
14189407Speter  return this;
14289407Speter}
14389407Speter
14489407Speterint Resolver::ModuleRepoRequest (Server *s)
14589407Speter{
146111748Sdes  s->PathnameResponse (REPO_DIR);
14789407Speter  return 0;
14889407Speter}
14989407Speter
15089407Speter// Deprecated resolver functions
15189407Speterint Resolver::ModuleExportRequest (Server *s, Flags, std::string &module)
15289407Speter{
15389407Speter  auto cmi = GetCMIName (module);
15489324Speter  s->PathnameResponse (cmi);
15589324Speter  return 0;
15689324Speter}
15789324Speter
15889324Speterint Resolver::ModuleImportRequest (Server *s, Flags, std::string &module)
15989324Speter{
16089407Speter  auto cmi = GetCMIName (module);
16189407Speter  s->PathnameResponse (cmi);
16289324Speter  return 0;
16389407Speter}
16489324Speter
16589324Speterint Resolver::ModuleCompiledRequest (Server *s, Flags, std::string &)
16689324Speter{
16789324Speter  s->OKResponse ();
16889324Speter  return 0;
16989324Speter}
17089324Speter
17189324Speterint Resolver::IncludeTranslateRequest (Server *s, Flags, std::string &include)
172104354Sscottl{
17389324Speter  bool xlate = false;
17489324Speter
17589324Speter  // This is not the most efficient
17689324Speter  auto cmi = GetCMIName (include);
17789324Speter  struct stat statbuf;
17889324Speter
17983651Speter#if HAVE_FSTATAT
18083651Speter  int fd_dir = open (REPO_DIR, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
1811541Srgrimes  if (fd_dir >= 0
18283651Speter      && fstatat (fd_dir, cmi.c_str (), &statbuf, 0) == 0
1831541Srgrimes      && S_ISREG (statbuf.st_mode))
1841541Srgrimes    // Sadly can't easily check if this process has read access,
18589324Speter    // except by trying to open it.
18689324Speter    xlate = true;
18789324Speter  if (fd_dir >= 0)
18889324Speter    close (fd_dir);
18989324Speter#else
19089324Speter  std::string append = REPO_DIR;
19189324Speter  append.push_back (DIR_SEPARATOR);
19289324Speter  append.append (cmi);
19389324Speter  if (stat (append.c_str (), &statbuf) == 0
1941541Srgrimes      || S_ISREG (statbuf.st_mode))
1951541Srgrimes    xlate = true;
19683651Speter#endif
1971541Srgrimes
19883651Speter  if (xlate)
19983651Speter    s->PathnameResponse (cmi);
2001541Srgrimes  else
20144246Speter    s->BoolResponse (false);
2021541Srgrimes
2031541Srgrimes  return 0;
20489324Speter}
2051541Srgrimes
20683651Spetervoid Resolver::ErrorResponse (Server *server, std::string &&msg)
20789324Speter{
2081541Srgrimes  server->ErrorResponse (msg);
20983651Speter}
21019449Sdfr
21189324Speter}
21231016Sphk