Lay groundwork for Windows support

Move Unix-specific code to util-unix.cpp, and place Windows equivalents
in util-win32.cpp.  Most of the Windows functions are just stubs at
the moment, and we need a build system that works on Windows.
This commit is contained in:
Andrew Ayer
2014-06-08 16:03:18 -07:00
parent c2a9e48de5
commit 0774ed018c
8 changed files with 415 additions and 211 deletions

212
util.cpp
View File

@@ -31,198 +31,7 @@
#include "git-crypt.hpp"
#include "util.hpp"
#include <string>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fstream>
void mkdir_parent (const std::string& path)
{
std::string::size_type slash(path.find('/', 1));
while (slash != std::string::npos) {
std::string prefix(path.substr(0, slash));
struct stat status;
if (stat(prefix.c_str(), &status) == 0) {
// already exists - make sure it's a directory
if (!S_ISDIR(status.st_mode)) {
throw System_error("mkdir_parent", prefix, ENOTDIR);
}
} else {
if (errno != ENOENT) {
throw System_error("mkdir_parent", prefix, errno);
}
// doesn't exist - mkdir it
if (mkdir(prefix.c_str(), 0777) == -1) {
throw System_error("mkdir", prefix, errno);
}
}
slash = path.find('/', slash + 1);
}
}
std::string readlink (const char* pathname)
{
std::vector<char> buffer(64);
ssize_t len;
while ((len = ::readlink(pathname, &buffer[0], buffer.size())) == static_cast<ssize_t>(buffer.size())) {
// buffer may have been truncated - grow and try again
buffer.resize(buffer.size() * 2);
}
if (len == -1) {
throw System_error("readlink", pathname, errno);
}
return std::string(buffer.begin(), buffer.begin() + len);
}
std::string our_exe_path ()
{
try {
return readlink("/proc/self/exe");
} catch (const System_error&) {
if (argv0[0] == '/') {
// argv[0] starts with / => it's an absolute path
return argv0;
} else if (std::strchr(argv0, '/')) {
// argv[0] contains / => it a relative path that should be resolved
char* resolved_path_p = realpath(argv0, NULL);
std::string resolved_path(resolved_path_p);
free(resolved_path_p);
return resolved_path;
} else {
// argv[0] is just a bare filename => not much we can do
return argv0;
}
}
}
int exec_command (const char* command, std::ostream& output)
{
int pipefd[2];
if (pipe(pipefd) == -1) {
throw System_error("pipe", "", errno);
}
pid_t child = fork();
if (child == -1) {
int fork_errno = errno;
close(pipefd[0]);
close(pipefd[1]);
throw System_error("fork", "", fork_errno);
}
if (child == 0) {
close(pipefd[0]);
if (pipefd[1] != 1) {
dup2(pipefd[1], 1);
close(pipefd[1]);
}
execl("/bin/sh", "sh", "-c", command, NULL);
perror("/bin/sh");
_exit(-1);
}
close(pipefd[1]);
char buffer[1024];
ssize_t bytes_read;
while ((bytes_read = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
output.write(buffer, bytes_read);
}
if (bytes_read == -1) {
int read_errno = errno;
close(pipefd[0]);
throw System_error("read", "", read_errno);
}
close(pipefd[0]);
int status = 0;
if (waitpid(child, &status, 0) == -1) {
throw System_error("waitpid", "", errno);
}
return status;
}
int exec_command_with_input (const char* command, const char* p, size_t len)
{
int pipefd[2];
if (pipe(pipefd) == -1) {
throw System_error("pipe", "", errno);
}
pid_t child = fork();
if (child == -1) {
int fork_errno = errno;
close(pipefd[0]);
close(pipefd[1]);
throw System_error("fork", "", fork_errno);
}
if (child == 0) {
close(pipefd[1]);
if (pipefd[0] != 0) {
dup2(pipefd[0], 0);
close(pipefd[0]);
}
execl("/bin/sh", "sh", "-c", command, NULL);
perror("/bin/sh");
_exit(-1);
}
close(pipefd[0]);
while (len > 0) {
ssize_t bytes_written = write(pipefd[1], p, len);
if (bytes_written == -1) {
int write_errno = errno;
close(pipefd[1]);
throw System_error("write", "", write_errno);
}
p += bytes_written;
len -= bytes_written;
}
close(pipefd[1]);
int status = 0;
if (waitpid(child, &status, 0) == -1) {
throw System_error("waitpid", "", errno);
}
return status;
}
bool successful_exit (int status)
{
return status != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
void open_tempfile (std::fstream& file, std::ios_base::openmode mode)
{
const char* tmpdir = getenv("TMPDIR");
size_t tmpdir_len = tmpdir ? std::strlen(tmpdir) : 0;
if (tmpdir_len == 0 || tmpdir_len > 4096) {
// no $TMPDIR or it's excessively long => fall back to /tmp
tmpdir = "/tmp";
tmpdir_len = 4;
}
std::vector<char> path_buffer(tmpdir_len + 18);
char* path = &path_buffer[0];
std::strcpy(path, tmpdir);
std::strcpy(path + tmpdir_len, "/git-crypt.XXXXXX");
mode_t old_umask = umask(0077);
int fd = mkstemp(path);
if (fd == -1) {
int mkstemp_errno = errno;
umask(old_umask);
throw System_error("mkstemp", "", mkstemp_errno);
}
umask(old_umask);
file.open(path, mode);
if (!file.is_open()) {
unlink(path);
close(fd);
throw System_error("std::fstream::open", path, 0);
}
unlink(path);
close(fd);
}
#include <iostream>
std::string escape_shell_arg (const std::string& str)
{
@@ -272,3 +81,22 @@ void write_be32 (std::ostream& out, uint32_t i)
out.write(reinterpret_cast<const char*>(buffer), 4);
}
static void init_std_streams_platform (); // platform-specific initialization
void init_std_streams ()
{
// The following two lines are essential for achieving good performance:
std::ios_base::sync_with_stdio(false);
std::cin.tie(0);
std::cin.exceptions(std::ios_base::badbit);
std::cout.exceptions(std::ios_base::badbit);
init_std_streams_platform();
}
#ifdef _WIN32
#include "util-win32.cpp"
#else
#include "util-unix.cpp"
#endif