// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build ignore
#include "fileutil.h"
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <limits.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#include <unordered_map>
#include "log.h"
#include "strutil.h"
bool Exists(StringPiece filename) {
CHECK(filename.size() < PATH_MAX);
struct stat st;
if (stat(filename.as_string().c_str(), &st) < 0) {
return false;
}
return true;
}
double GetTimestampFromStat(const struct stat& st) {
#if defined(__linux__)
return st.st_mtime + st.st_mtim.tv_nsec * 0.001 * 0.001 * 0.001;
#else
return st.st_mtime;
#endif
}
double GetTimestamp(StringPiece filename) {
CHECK(filename.size() < PATH_MAX);
struct stat st;
if (stat(filename.as_string().c_str(), &st) < 0) {
return -2.0;
}
return GetTimestampFromStat(st);
}
int RunCommand(const string& shell, const string& cmd,
RedirectStderr redirect_stderr,
string* s) {
string cmd_escaped = cmd;
EscapeShell(&cmd_escaped);
string cmd_with_shell = shell + " -c \"" + cmd_escaped + "\"";
const char* argv[] = {
"/bin/sh", "-c", cmd_with_shell.c_str(), NULL
};
int pipefd[2];
if (pipe(pipefd) != 0)
PERROR("pipe failed");
int pid;
if ((pid = vfork())) {
int status;
close(pipefd[1]);
while (true) {
int result = waitpid(pid, &status, WNOHANG);
if (result < 0)
PERROR("waitpid failed");
while (true) {
char buf[4096];
ssize_t r = read(pipefd[0], buf, 4096);
if (r < 0)
PERROR("read failed");
if (r == 0)
break;
s->append(buf, buf+r);
}
if (result != 0) {
break;
}
}
close(pipefd[0]);
return status;
} else {
close(pipefd[0]);
if (redirect_stderr == RedirectStderr::STDOUT) {
if (dup2(pipefd[1], 2) < 0)
PERROR("dup2 failed");
} else if (redirect_stderr == RedirectStderr::DEV_NULL) {
int fd = open("/dev/null", O_WRONLY);
if (dup2(fd, 2) < 0)
PERROR("dup2 failed");
close(fd);
}
if (dup2(pipefd[1], 1) < 0)
PERROR("dup2 failed");
close(pipefd[1]);
execvp(argv[0], const_cast<char**>(argv));
PLOG("execvp for %s failed", argv[0]);
kill(getppid(), SIGTERM);
_exit(1);
}
}
void GetExecutablePath(string* path) {
#if defined(__linux__)
char mypath[PATH_MAX + 1];
ssize_t l = readlink("/proc/self/exe", mypath, PATH_MAX);
if (l < 0) {
PERROR("readlink for /proc/self/exe");
}
mypath[l] = '\0';
*path = mypath;
#elif defined(__APPLE__)
char mypath[PATH_MAX + 1];
uint32_t size = PATH_MAX;
if (_NSGetExecutablePath(mypath, &size) != 0) {
ERROR("_NSGetExecutablePath failed");
}
mypath[size] = 0;
*path = mypath;
#else
#error "Unsupported OS"
#endif
}
namespace {
class GlobCache {
public:
~GlobCache() {
Clear();
}
void Get(const char* pat, vector<string>** files) {
auto p = cache_.emplace(pat, nullptr);
if (p.second) {
vector<string>* files = p.first->second = new vector<string>;
if (strcspn(pat, "?*[\\") != strlen(pat)) {
glob_t gl;
glob(pat, GLOB_NOSORT, NULL, &gl);
for (size_t i = 0; i < gl.gl_pathc; i++) {
files->push_back(gl.gl_pathv[i]);
}
globfree(&gl);
} else {
if (Exists(pat))
files->push_back(pat);
}
}
*files = p.first->second;
}
const unordered_map<string, vector<string>*>& GetAll() const {
return cache_;
}
void Clear() {
for (auto& p : cache_) {
delete p.second;
}
cache_.clear();
}
private:
unordered_map<string, vector<string>*> cache_;
};
static GlobCache g_gc;
} // namespace
void Glob(const char* pat, vector<string>** files) {
g_gc.Get(pat, files);
}
const unordered_map<string, vector<string>*>& GetAllGlobCache() {
return g_gc.GetAll();
}
void ClearGlobCache() {
g_gc.Clear();
}