1 // CODYlib -*- mode:c++ -*-
2 // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
3 // License: Apache v2.0
11 #include <sys/types.h>
13 #if ((defined (__unix__) \
14 && defined _POSIX_C_SOURCE \
15 && (_POSIX_C_SOURCE - 0) >= 200809L) \
16 || (defined (__Apple__) \
17 && defined (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) \
18 && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101000))
20 #define HAVE_FSTATAT 1
22 #define HAVE_FSTATAT 0
28 inline bool IsDirSep (char c
)
30 return c
== '/' || c
== '\\';
32 inline bool IsAbsPath (char const *str
)
34 // IIRC windows has the concept of per-drive current directories,
35 // which make drive-using paths confusing. Let's not get into that.
37 || (((str
[0] >= 'A' && str
[0] <= 'Z')
38 || (str
[0] >= 'a' && str
[0] <= 'z'))&& str
[1] == ':');
41 inline bool IsDirSep (char c
)
45 inline bool IsAbsPath (char const *str
)
47 return IsDirSep (str
[0]);
51 constexpr char DIR_SEPARATOR
= '/';
53 constexpr char DOT_REPLACE
= ','; // Replace . directories
54 constexpr char COLON_REPLACE
= '-'; // Replace : (partition char)
55 constexpr char const REPO_DIR
[] = "cmi.cache";
59 Resolver::~Resolver ()
63 char const *Resolver::GetCMISuffix ()
68 std::string
Resolver::GetCMIName (std::string
const &module
)
72 result
.reserve (module
.size () + 8);
73 bool is_header
= false;
76 if (IsAbsPath (module
.c_str ()))
77 is_header
= is_abs
= true;
78 else if (module
.front () == '.' && IsDirSep (module
.c_str ()[1]))
83 result
.push_back ('.');
84 result
.append (module
);
87 result
= std::move (module
);
92 result
[0] = DOT_REPLACE
;
94 /* Map .. to DOT_REPLACE, DOT_REPLACE. */
95 for (size_t ix
= 1; ; ix
++)
97 ix
= result
.find ('.', ix
);
98 if (ix
== result
.npos
)
100 if (ix
+ 2 > result
.size ())
102 if (result
[ix
+ 1] != '.')
104 if (!IsDirSep (result
[ix
- 1]))
106 if (!IsDirSep (result
[ix
+ 2]))
108 result
[ix
] = DOT_REPLACE
;
109 result
[ix
+ 1] = DOT_REPLACE
;
112 else if (COLON_REPLACE
!= ':')
114 // There can only be one colon in a module name
115 auto colon
= result
.find (':');
116 if (colon
!= result
.npos
)
117 result
[colon
] = COLON_REPLACE
;
120 if (char const *suffix
= GetCMISuffix ())
122 result
.push_back ('.');
123 result
.append (suffix
);
129 void Resolver::WaitUntilReady (Server
*)
133 Resolver
*Resolver::ConnectRequest (Server
*s
, unsigned version
,
134 std::string
&, std::string
&)
136 if (version
> Version
)
137 s
->ErrorResponse ("version mismatch");
139 s
->ConnectResponse ("default");
144 int Resolver::ModuleRepoRequest (Server
*s
)
146 s
->PathnameResponse (REPO_DIR
);
150 // Deprecated resolver functions
151 int Resolver::ModuleExportRequest (Server
*s
, Flags
, std::string
&module
)
153 auto cmi
= GetCMIName (module
);
154 s
->PathnameResponse (cmi
);
158 int Resolver::ModuleImportRequest (Server
*s
, Flags
, std::string
&module
)
160 auto cmi
= GetCMIName (module
);
161 s
->PathnameResponse (cmi
);
165 int Resolver::ModuleCompiledRequest (Server
*s
, Flags
, std::string
&)
171 int Resolver::IncludeTranslateRequest (Server
*s
, Flags
, std::string
&include
)
175 // This is not the most efficient
176 auto cmi
= GetCMIName (include
);
180 int fd_dir
= open (REPO_DIR
, O_RDONLY
| O_CLOEXEC
| O_DIRECTORY
);
182 && fstatat (fd_dir
, cmi
.c_str (), &statbuf
, 0) == 0
183 && S_ISREG (statbuf
.st_mode
))
184 // Sadly can't easily check if this process has read access,
185 // except by trying to open it.
190 std::string append
= REPO_DIR
;
191 append
.push_back (DIR_SEPARATOR
);
193 if (stat (append
.c_str (), &statbuf
) == 0
194 || S_ISREG (statbuf
.st_mode
))
199 s
->PathnameResponse (cmi
);
201 s
->BoolResponse (false);
206 void Resolver::ErrorResponse (Server
*server
, std::string
&&msg
)
208 server
->ErrorResponse (msg
);