2017-12-17 20:40:21 +00:00
|
|
|
#if defined(__unix__) || defined(__APPLE__)
|
2017-03-04 01:45:20 +00:00
|
|
|
#include "platform.h"
|
|
|
|
|
2017-03-25 20:15:00 +00:00
|
|
|
#include "utils.h"
|
2017-03-04 01:45:20 +00:00
|
|
|
|
2017-12-29 22:46:21 +00:00
|
|
|
#include "loguru.hpp"
|
2017-07-28 02:14:33 +00:00
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <pthread.h>
|
2017-12-29 22:46:21 +00:00
|
|
|
#if defined(__FreeBSD__)
|
2018-01-11 02:43:01 +00:00
|
|
|
#include <pthread_np.h>
|
|
|
|
#include <sys/thr.h>
|
2017-12-29 22:46:21 +00:00
|
|
|
#elif defined(__OpenBSD__)
|
2018-01-11 02:43:01 +00:00
|
|
|
#include <pthread_np.h>
|
2017-12-29 22:46:21 +00:00
|
|
|
#endif
|
2017-09-22 01:14:57 +00:00
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <semaphore.h>
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <signal.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2017-04-18 17:21:53 +00:00
|
|
|
#include <dirent.h>
|
2017-04-20 16:47:24 +00:00
|
|
|
#include <sys/stat.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <sys/types.h> // required for stat.h
|
2018-02-20 00:19:57 +00:00
|
|
|
#include <sys/wait.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
|
|
|
|
#include <errno.h>
|
2017-04-21 06:32:18 +00:00
|
|
|
#include <fcntl.h>
|
2017-04-18 02:59:48 +00:00
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <semaphore.h>
|
|
|
|
#include <sys/mman.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
|
2017-12-17 20:40:21 +00:00
|
|
|
#if defined(__FreeBSD__)
|
2017-12-23 16:01:43 +00:00
|
|
|
#include <sys/param.h> // MAXPATHLEN
|
|
|
|
#include <sys/sysctl.h> // sysctl
|
2017-12-17 20:40:21 +00:00
|
|
|
#elif defined(__linux__)
|
2017-12-23 16:01:43 +00:00
|
|
|
#include <malloc.h>
|
2017-10-31 22:43:07 +00:00
|
|
|
#endif
|
|
|
|
|
2017-12-29 22:46:21 +00:00
|
|
|
#include <string>
|
|
|
|
|
2017-11-30 18:40:42 +00:00
|
|
|
namespace {
|
|
|
|
|
2017-12-01 17:50:39 +00:00
|
|
|
// Returns the canonicalized absolute pathname, without expanding symbolic
|
|
|
|
// links. This is a variant of realpath(2), C++ rewrite of
|
2017-11-30 18:40:42 +00:00
|
|
|
// https://github.com/freebsd/freebsd/blob/master/lib/libc/stdlib/realpath.c
|
2018-03-31 03:16:33 +00:00
|
|
|
std::optional<std::string> RealPathNotExpandSymlink(std::string path) {
|
2017-11-30 18:40:42 +00:00
|
|
|
if (path.empty()) {
|
|
|
|
errno = EINVAL;
|
2018-03-31 03:16:33 +00:00
|
|
|
return std::nullopt;
|
2017-11-30 18:40:42 +00:00
|
|
|
}
|
|
|
|
if (path[0] == '\0') {
|
|
|
|
errno = ENOENT;
|
2018-03-31 03:16:33 +00:00
|
|
|
return std::nullopt;
|
2017-11-30 18:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Do not use PATH_MAX because it is tricky on Linux.
|
|
|
|
// See https://eklitzke.org/path-max-is-tricky
|
|
|
|
char tmp[1024];
|
|
|
|
std::string resolved;
|
|
|
|
size_t i = 0;
|
|
|
|
struct stat sb;
|
|
|
|
if (path[0] == '/') {
|
|
|
|
resolved = "/";
|
|
|
|
i = 1;
|
|
|
|
} else {
|
|
|
|
if (!getcwd(tmp, sizeof tmp))
|
2018-03-31 03:16:33 +00:00
|
|
|
return std::nullopt;
|
2017-11-30 18:40:42 +00:00
|
|
|
resolved = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (i < path.size()) {
|
|
|
|
auto j = path.find('/', i);
|
|
|
|
if (j == std::string::npos)
|
|
|
|
j = path.size();
|
|
|
|
auto next_token = path.substr(i, j - i);
|
|
|
|
i = j + 1;
|
|
|
|
if (resolved.back() != '/')
|
|
|
|
resolved += '/';
|
|
|
|
if (next_token.empty() || next_token == ".") {
|
|
|
|
// Handle consequential slashes and "."
|
|
|
|
continue;
|
|
|
|
} else if (next_token == "..") {
|
|
|
|
// Strip the last path component except when it is single "/"
|
|
|
|
if (resolved.size() > 1)
|
|
|
|
resolved.resize(resolved.rfind('/', resolved.size() - 2) + 1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Append the next path component.
|
|
|
|
// Here we differ from realpath(3), we use stat(2) instead of
|
|
|
|
// lstat(2) because we do not want to resolve symlinks.
|
|
|
|
resolved += next_token;
|
|
|
|
if (stat(resolved.c_str(), &sb) != 0)
|
2018-03-31 03:16:33 +00:00
|
|
|
return std::nullopt;
|
2017-11-30 18:40:42 +00:00
|
|
|
if (!S_ISDIR(sb.st_mode) && j < path.size()) {
|
|
|
|
errno = ENOTDIR;
|
2018-03-31 03:16:33 +00:00
|
|
|
return std::nullopt;
|
2017-11-30 18:40:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove trailing slash except when a single "/".
|
|
|
|
if (resolved.size() > 1 && resolved.back() == '/')
|
|
|
|
resolved.pop_back();
|
|
|
|
return resolved;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void PlatformInit() {}
|
2017-03-28 01:47:12 +00:00
|
|
|
|
2017-12-14 22:35:21 +00:00
|
|
|
#ifdef __APPLE__
|
2017-12-23 16:01:43 +00:00
|
|
|
extern "C" int _NSGetExecutablePath(char* buf, uint32_t* bufsize);
|
2017-12-14 22:35:21 +00:00
|
|
|
#endif
|
|
|
|
|
2017-12-23 16:01:43 +00:00
|
|
|
// See
|
|
|
|
// https://stackoverflow.com/questions/143174/how-do-i-get-the-directory-that-a-program-is-running-from
|
2017-12-14 22:35:21 +00:00
|
|
|
std::string GetExecutablePath() {
|
2017-12-17 20:40:21 +00:00
|
|
|
#ifdef __APPLE__
|
2017-12-14 22:35:21 +00:00
|
|
|
uint32_t size = 0;
|
|
|
|
_NSGetExecutablePath(nullptr, &size);
|
2017-12-23 16:01:43 +00:00
|
|
|
char* buffer = new char[size];
|
2017-12-14 22:35:21 +00:00
|
|
|
_NSGetExecutablePath(buffer, &size);
|
2018-01-21 04:37:24 +00:00
|
|
|
char* resolved = realpath(buffer, nullptr);
|
|
|
|
std::string result(resolved);
|
2017-12-14 22:35:21 +00:00
|
|
|
delete[] buffer;
|
2018-01-21 04:37:24 +00:00
|
|
|
free(resolved);
|
2017-12-14 22:35:21 +00:00
|
|
|
return result;
|
2017-12-17 20:40:21 +00:00
|
|
|
#elif defined(__FreeBSD__)
|
|
|
|
static const int name[] = {
|
2018-01-11 02:43:01 +00:00
|
|
|
CTL_KERN,
|
|
|
|
KERN_PROC,
|
|
|
|
KERN_PROC_PATHNAME,
|
|
|
|
-1,
|
2017-12-17 20:40:21 +00:00
|
|
|
};
|
|
|
|
char path[MAXPATHLEN];
|
|
|
|
size_t len = sizeof(path);
|
|
|
|
path[0] = '\0';
|
|
|
|
(void)sysctl(name, 4, path, &len, NULL, 0);
|
|
|
|
return std::string(path);
|
|
|
|
#else
|
|
|
|
char buffer[PATH_MAX] = {0};
|
2018-01-11 02:43:01 +00:00
|
|
|
if (-1 == readlink("/proc/self/exe", buffer, PATH_MAX))
|
2018-01-03 12:38:01 +00:00
|
|
|
return "";
|
|
|
|
return buffer;
|
2017-12-14 22:35:21 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-03-28 01:47:12 +00:00
|
|
|
std::string NormalizePath(const std::string& path) {
|
2018-03-31 03:16:33 +00:00
|
|
|
std::optional<std::string> resolved = RealPathNotExpandSymlink(path);
|
2017-11-30 18:40:42 +00:00
|
|
|
return resolved ? *resolved : path;
|
2017-03-28 01:47:12 +00:00
|
|
|
}
|
|
|
|
|
2018-04-08 00:10:54 +00:00
|
|
|
void SetThreadName(const std::string& thread_name) {
|
2017-07-28 02:14:33 +00:00
|
|
|
loguru::set_thread_name(thread_name.c_str());
|
2017-12-29 22:35:20 +00:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
pthread_setname_np(thread_name.c_str());
|
|
|
|
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
|
|
|
pthread_set_name_np(pthread_self(), thread_name.c_str());
|
|
|
|
#elif defined(__linux__)
|
|
|
|
pthread_setname_np(pthread_self(), thread_name.c_str());
|
2017-04-18 17:21:53 +00:00
|
|
|
#endif
|
2017-04-16 22:48:54 +00:00
|
|
|
}
|
|
|
|
|
2017-08-17 18:02:47 +00:00
|
|
|
void FreeUnusedMemory() {
|
2018-01-04 18:55:11 +00:00
|
|
|
#if defined(__GLIBC__)
|
2017-08-17 18:02:47 +00:00
|
|
|
malloc_trim(0);
|
2017-10-31 22:43:07 +00:00
|
|
|
#endif
|
2017-08-17 18:02:47 +00:00
|
|
|
}
|
|
|
|
|
2018-01-07 07:42:42 +00:00
|
|
|
void TraceMe() {
|
|
|
|
// If the environment variable is defined, wait for a debugger.
|
2018-03-31 03:16:33 +00:00
|
|
|
// In gdb, you need to invoke `signal SIGCONT` if you want ccls to continue
|
2018-01-07 07:42:42 +00:00
|
|
|
// after detaching.
|
2018-04-30 04:49:03 +00:00
|
|
|
const char* traceme = getenv("CCLS_TRACEME");
|
|
|
|
if (traceme)
|
|
|
|
raise(traceme[0] == 's' ? SIGSTOP : SIGTSTP);
|
2018-01-07 07:42:42 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 00:19:57 +00:00
|
|
|
std::string GetExternalCommandOutput(const std::vector<std::string>& command,
|
|
|
|
std::string_view input) {
|
|
|
|
int pin[2], pout[2];
|
2018-04-02 03:55:10 +00:00
|
|
|
if (pipe(pin) < 0) {
|
|
|
|
perror("pipe(stdin)");
|
2018-02-20 01:19:50 +00:00
|
|
|
return "";
|
2018-04-02 03:55:10 +00:00
|
|
|
}
|
2018-02-20 01:19:50 +00:00
|
|
|
if (pipe(pout) < 0) {
|
2018-04-02 03:55:10 +00:00
|
|
|
perror("pipe(stdout)");
|
2018-02-20 01:19:50 +00:00
|
|
|
close(pin[0]);
|
|
|
|
close(pin[1]);
|
|
|
|
return "";
|
|
|
|
}
|
2018-02-20 00:19:57 +00:00
|
|
|
pid_t child = fork();
|
|
|
|
if (child == 0) {
|
|
|
|
dup2(pout[0], 0);
|
|
|
|
dup2(pin[1], 1);
|
|
|
|
close(pin[0]);
|
|
|
|
close(pin[1]);
|
|
|
|
close(pout[0]);
|
|
|
|
close(pout[1]);
|
|
|
|
auto argv = new char*[command.size() + 1];
|
|
|
|
for (size_t i = 0; i < command.size(); i++)
|
|
|
|
argv[i] = const_cast<char*>(command[i].c_str());
|
|
|
|
argv[command.size()] = nullptr;
|
|
|
|
execvp(argv[0], argv);
|
|
|
|
_Exit(127);
|
|
|
|
}
|
|
|
|
close(pin[1]);
|
|
|
|
close(pout[0]);
|
2018-02-20 03:06:48 +00:00
|
|
|
// O_NONBLOCK is disabled, write(2) blocks until all bytes are written.
|
|
|
|
(void)write(pout[1], input.data(), input.size());
|
2018-02-20 00:19:57 +00:00
|
|
|
close(pout[1]);
|
|
|
|
std::string ret;
|
|
|
|
char buf[4096];
|
|
|
|
ssize_t n;
|
|
|
|
while ((n = read(pin[0], buf, sizeof buf)) > 0)
|
|
|
|
ret.append(buf, n);
|
2018-04-02 03:55:10 +00:00
|
|
|
close(pin[0]);
|
2018-02-20 00:19:57 +00:00
|
|
|
waitpid(child, NULL, 0);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-21 17:06:22 +00:00
|
|
|
#endif
|