211 lines
4.9 KiB
C++
211 lines
4.9 KiB
C++
// 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& shellflag,
|
|
const string& cmd, RedirectStderr redirect_stderr,
|
|
string* s) {
|
|
const char* argv[] = { NULL, NULL, NULL, NULL };
|
|
string cmd_with_shell;
|
|
if (shell[0] != '/' || shell.find_first_of(" $") != string::npos) {
|
|
string cmd_escaped = cmd;
|
|
EscapeShell(&cmd_escaped);
|
|
cmd_with_shell = shell + " " + shellflag + " \"" + cmd_escaped + "\"";
|
|
argv[0] = "/bin/sh";
|
|
argv[1] = "-c";
|
|
argv[2] = cmd_with_shell.c_str();
|
|
} else {
|
|
// If the shell isn't complicated, we don't need to wrap in /bin/sh
|
|
argv[0] = shell.c_str();
|
|
argv[1] = shellflag.c_str();
|
|
argv[2] = cmd.c_str();
|
|
}
|
|
|
|
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 = HANDLE_EINTR(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, 0, 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();
|
|
}
|