src/logging.cpp \
src/manifest.cpp \
src/result.cpp \
- src/stats.cpp
+ src/stats.cpp \
+ src/win32compat.cpp
generated_sources = \
src/version.cpp
third_party_sources = \
#include <algorithm>
#include <fstream>
+#ifdef _WIN32
+# include "win32compat.hpp"
+#endif
+
using nonstd::string_view;
namespace {
}
}
+size_t
+path_max(const char* path)
+{
+#ifdef PATH_MAX
+ (void)path;
+ return PATH_MAX;
+#elif defined(MAXPATHLEN)
+ (void)path;
+ return MAXPATHLEN;
+#elif defined(_PC_PATH_MAX)
+ long maxlen = pathconf(path, _PC_PATH_MAX);
+ return maxlen >= 4096 ? maxlen : 4096;
+#endif
+}
+
} // namespace
namespace Util {
std::istreambuf_iterator<char>());
}
+std::string
+real_path(const std::string& path, bool return_empty_on_error)
+{
+ const char* c_path = path.c_str();
+ size_t buffer_size = path_max(c_path);
+ std::unique_ptr<char[]> managed_buffer(new char[buffer_size]);
+ char* buffer = managed_buffer.get();
+ char* resolved = nullptr;
+
+#if HAVE_REALPATH
+ resolved = realpath(c_path, buffer);
+#elif defined(_WIN32)
+ if (c_path[0] == '/') {
+ c_path++; // Skip leading slash.
+ }
+ HANDLE path_handle = CreateFile(c_path,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (INVALID_HANDLE_VALUE != path_handle) {
+# ifdef HAVE_GETFINALPATHNAMEBYHANDLEW
+ GetFinalPathNameByHandle(
+ path_handle, buffer, buffer_size, FILE_NAME_NORMALIZED);
+# else
+ GetFileNameFromHandle(path_handle, buffer, buffer_size);
+# endif
+ CloseHandle(path_handle);
+ resolved = buffer + 4; // Strip \\?\ from the file name.
+ } else {
+ snprintf(buffer, buffer_size, "%s", c_path);
+ resolved = buffer;
+ }
+#else
+ // Yes, there are such systems. This replacement relies on the fact that when
+ // we call x_realpath we only care about symlinks.
+ {
+ ssize_t len = readlink(c_path, buffer, buffer_size - 1);
+ if (len != -1) {
+ buffer[len] = 0;
+ resolved = buffer;
+ }
+ }
+#endif
+
+ return resolved ? resolved : (return_empty_on_error ? "" : path);
+}
+
string_view
remove_extension(string_view path)
{
// Throws Error on error.
std::string read_file(const std::string& path);
+// Return a canonicalized absolute path of `path`. On error (e.g. if the `path`
+// doesn't exist) the empty string is returned if return_empty_on_error is true,
+// otherwise `path` unmodified.
+std::string real_path(const std::string& path,
+ bool return_empty_on_error = false);
+
// Return a view into `path` containing the given path without the filename
// extension as determined by `get_extension()`.
nonstd::string_view remove_extension(nonstd::string_view path);
if (!current_working_dir) {
char* cwd = get_cwd();
if (cwd) {
- current_working_dir = x_realpath(cwd);
+ current_working_dir = x_strdup(Util::real_path(cwd).c_str());
free(cwd);
}
if (!current_working_dir) {
char* dir = nullptr;
char* path_suffix = nullptr;
- char* canon_path = nullptr;
char* relpath = nullptr;
- // x_realpath only works for existing paths, so if path doesn't exist, try
- // x_dirname(path) and assemble the path afterwards. We only bother to try
+ // Util::real_path only works for existing paths, so if path doesn't exist,
+ // try x_dirname(path) and assemble the path afterwards. We only bother to try
// canonicalizing one of these two paths since a compiler path argument
// typically only makes sense if path or x_dirname(path) exists.
std::string result;
- canon_path = x_realpath(path);
- if (canon_path) {
- relpath = get_relative_path(get_current_working_dir(), canon_path);
+ std::string canon_path = Util::real_path(path, true);
+ if (!canon_path.empty()) {
+ relpath = get_relative_path(get_current_working_dir(), canon_path.c_str());
if (path_suffix) {
result = fmt::format("{}/{}", relpath, path_suffix);
} else {
#endif
free(dir);
free(path_suffix);
- free(canon_path);
free(relpath);
return result;
// Possibly hash the coverage data file path.
if (ctx.args_info.generating_coverage && ctx.args_info.profile_arcs) {
- char* dir = x_dirname(ctx.args_info.output_obj.c_str());
+ std::string dir;
if (!ctx.args_info.profile_dir.empty()) {
- dir = x_strdup(ctx.args_info.profile_dir.c_str());
+ dir = ctx.args_info.profile_dir;
} else {
- char* real_dir = x_realpath(dir);
- free(dir);
- dir = real_dir;
- }
- if (dir) {
- string_view base_name = Util::base_name(ctx.args_info.output_obj);
- string_view p = Util::remove_extension(base_name);
- std::string gcda_path = fmt::format("{}/{}.gcda", dir, p);
- cc_log("Hashing coverage path %s", gcda_path.c_str());
- hash_delimiter(hash, "gcda");
- hash_string(hash, gcda_path);
- free(dir);
+ dir =
+ Util::real_path(std::string(Util::dir_name(ctx.args_info.output_obj)));
}
+ string_view stem =
+ Util::remove_extension(Util::base_name(ctx.args_info.output_obj));
+ std::string gcda_path = fmt::format("{}/{}.gcda", dir, stem);
+ cc_log("Hashing coverage path %s", gcda_path.c_str());
+ hash_delimiter(hash, "gcda");
+ hash_string(hash, gcda_path);
}
// Possibly hash the sanitize blacklist file path.
const char* arg_profile_dir = strchr(argv[i], '=');
if (arg_profile_dir) {
// Convert to absolute path.
- char* dir = x_realpath(arg_profile_dir + 1);
- if (!dir) {
- // Directory doesn't exist.
- dir = x_strdup(arg_profile_dir + 1);
- }
+ std::string dir = Util::real_path(arg_profile_dir + 1);
// We can get a better hit rate by using the real path here.
free(arg);
char* option = x_strndup(argv[i], arg_profile_dir - argv[i]);
- arg = format("%s=%s", option, dir);
+ arg = format("%s=%s", option, dir.c_str());
cc_log("Rewriting %s to %s", argv[i], arg);
free(option);
- free(dir);
}
bool supported_profile_option = false;
return x_strdup(namebuf);
}
#else
+ assert(exclude_name);
char* fname = format("%s/%s", tok, name);
auto st1 = Stat::lstat(fname);
auto st2 = Stat::stat(fname);
// Look for a normal executable file.
if (st1 && st2 && st2.is_regular() && access(fname, X_OK) == 0) {
if (st1.is_symlink()) {
- char* buf = x_realpath(fname);
- if (buf) {
- string_view p = Util::base_name(buf);
- if (p == exclude_name) {
- // It's a link to "ccache"!
- free(buf);
- continue;
- }
- free(buf);
+ std::string real_path = Util::real_path(fname, true);
+ if (Util::base_name(real_path) == exclude_name) {
+ // It's a link to "ccache"!
+ continue;
}
}
# endif
#endif
-#ifdef _WIN32
-# include <psapi.h>
-# include <sys/locking.h>
-# include <tchar.h>
-#endif
-
static long
path_max(const char* path)
{
return true;
}
-#if !defined(HAVE_REALPATH) && defined(_WIN32) \
- && !defined(HAVE_GETFINALPATHNAMEBYHANDLEW)
-static BOOL
-GetFileNameFromHandle(HANDLE file_handle, TCHAR* filename, WORD cch_filename)
-{
- BOOL success = FALSE;
-
- // Get the file size.
- DWORD file_size_hi = 0;
- DWORD file_size_lo = GetFileSize(file_handle, &file_size_hi);
- if (file_size_lo == 0 && file_size_hi == 0) {
- // Cannot map a file with a length of zero.
- return FALSE;
- }
-
- // Create a file mapping object.
- HANDLE file_map =
- CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 1, NULL);
- if (!file_map) {
- return FALSE;
- }
-
- // Create a file mapping to get the file name.
- void* mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1);
- if (mem) {
- if (GetMappedFileName(GetCurrentProcess(), mem, filename, cch_filename)) {
- // Translate path with device name to drive letters.
- TCHAR temp[512];
- temp[0] = '\0';
-
- if (GetLogicalDriveStrings(512 - 1, temp)) {
- TCHAR name[MAX_PATH];
- TCHAR drive[3] = TEXT(" :");
- BOOL found = FALSE;
- TCHAR* p = temp;
-
- do {
- // Copy the drive letter to the template string.
- *drive = *p;
-
- // Look up each device name.
- if (QueryDosDevice(drive, name, MAX_PATH)) {
- size_t name_len = _tcslen(name);
- if (name_len < MAX_PATH) {
- found = _tcsnicmp(filename, name, name_len) == 0
- && *(filename + name_len) == _T('\\');
- if (found) {
- // Reconstruct filename using temp_file and replace device path
- // with DOS path.
- TCHAR temp_file[MAX_PATH];
- _sntprintf(temp_file,
- MAX_PATH - 1,
- TEXT("%s%s"),
- drive,
- filename + name_len);
- strcpy(filename, temp_file);
- }
- }
- }
-
- // Go to the next NULL character.
- while (*p++) {
- // Do nothing.
- }
- } while (!found && *p); // End of string.
- }
- }
- success = TRUE;
- UnmapViewOfFile(mem);
- }
-
- CloseHandle(file_map);
- return success;
-}
-#endif
-
-// A sane realpath() function, trying to cope with stupid path limits and a
-// broken API. Caller frees.
-char*
-x_realpath(const char* path)
-{
- long maxlen = path_max(path);
- char* ret = static_cast<char*>(x_malloc(maxlen));
- char* p;
-
-#if HAVE_REALPATH
- p = realpath(path, ret);
-#elif defined(_WIN32)
- if (path[0] == '/') {
- path++; // Skip leading slash.
- }
- HANDLE path_handle = CreateFile(path,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if (INVALID_HANDLE_VALUE != path_handle) {
-# ifdef HAVE_GETFINALPATHNAMEBYHANDLEW
- GetFinalPathNameByHandle(path_handle, ret, maxlen, FILE_NAME_NORMALIZED);
-# else
- GetFileNameFromHandle(path_handle, ret, maxlen);
-# endif
- CloseHandle(path_handle);
- p = ret + 4; // Strip \\?\ from the file name.
- } else {
- snprintf(ret, maxlen, "%s", path);
- p = ret;
- }
-#else
- // Yes, there are such systems. This replacement relies on the fact that when
- // we call x_realpath we only care about symlinks.
- {
- int len = readlink(path, ret, maxlen - 1);
- if (len == -1) {
- free(ret);
- return NULL;
- }
- ret[len] = 0;
- p = ret;
- }
-#endif
- if (p) {
- p = x_strdup(p);
- free(ret);
- return p;
- }
- free(ret);
- return NULL;
-}
-
// A getcwd that will returns an allocated buffer.
char*
gnu_getcwd(void)
char* format_human_readable_size(uint64_t size);
char* format_parsable_size_with_suffix(uint64_t size);
bool parse_size_with_suffix(const char* str, uint64_t* size);
-char* x_realpath(const char* path);
char* gnu_getcwd();
#ifndef HAVE_LOCALTIME_R
struct tm* localtime_r(const time_t* timep, struct tm* result);
--- /dev/null
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "win32compat.hpp"
+
+#ifdef _WIN32
+
+# include <psapi.h>
+# include <sys/locking.h>
+# include <tchar.h>
+
+# if !defined(HAVE_REALPATH) && !defined(HAVE_GETFINALPATHNAMEBYHANDLEW)
+BOOL
+GetFileNameFromHandle(HANDLE file_handle, TCHAR* filename, WORD cch_filename)
+{
+ BOOL success = FALSE;
+
+ // Get the file size.
+ DWORD file_size_hi = 0;
+ DWORD file_size_lo = GetFileSize(file_handle, &file_size_hi);
+ if (file_size_lo == 0 && file_size_hi == 0) {
+ // Cannot map a file with a length of zero.
+ return FALSE;
+ }
+
+ // Create a file mapping object.
+ HANDLE file_map =
+ CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 1, NULL);
+ if (!file_map) {
+ return FALSE;
+ }
+
+ // Create a file mapping to get the file name.
+ void* mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1);
+ if (mem) {
+ if (GetMappedFileName(GetCurrentProcess(), mem, filename, cch_filename)) {
+ // Translate path with device name to drive letters.
+ TCHAR temp[512];
+ temp[0] = '\0';
+
+ if (GetLogicalDriveStrings(512 - 1, temp)) {
+ TCHAR name[MAX_PATH];
+ TCHAR drive[3] = TEXT(" :");
+ BOOL found = FALSE;
+ TCHAR* p = temp;
+
+ do {
+ // Copy the drive letter to the template string.
+ *drive = *p;
+
+ // Look up each device name.
+ if (QueryDosDevice(drive, name, MAX_PATH)) {
+ size_t name_len = _tcslen(name);
+ if (name_len < MAX_PATH) {
+ found = _tcsnicmp(filename, name, name_len) == 0
+ && *(filename + name_len) == _T('\\');
+ if (found) {
+ // Reconstruct filename using temp_file and replace device path
+ // with DOS path.
+ TCHAR temp_file[MAX_PATH];
+ _sntprintf(temp_file,
+ MAX_PATH - 1,
+ TEXT("%s%s"),
+ drive,
+ filename + name_len);
+ strcpy(filename, temp_file);
+ }
+ }
+ }
+
+ // Go to the next NULL character.
+ while (*p++) {
+ // Do nothing.
+ }
+ } while (!found && *p); // End of string.
+ }
+ }
+ success = TRUE;
+ UnmapViewOfFile(mem);
+ }
+
+ CloseHandle(file_map);
+ return success;
+}
+# endif
+
+#endif
--- /dev/null
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#pragma once
+
+#ifdef _WIN32
+# include "system.hpp"
+
+BOOL
+GetFileNameFromHandle(HANDLE file_handle, TCHAR* filename, WORD cch_filename);
+
+#endif
#include "../src/Config.hpp"
#include "../src/Context.hpp"
+#include "../src/Util.hpp"
#include "../src/args.hpp"
#include "../src/ccache.hpp"
#include "../src/legacy_globals.hpp"
struct args* act_extra = NULL;
struct args* act_cc = NULL;
- char *s, *path;
+ char* s;
create_file("foo.c", "");
mkdir("some", 0777);
mkdir("some/dir", 0777);
- path = x_realpath("some/dir");
- s = format("-fprofile-generate=%s", path);
- free(path);
+ std::string path = Util::real_path("some/dir");
+ s = format("-fprofile-generate=%s", path.c_str());
args_add(exp_cpp, s);
args_add(exp_cc, s);
args_add(exp_cc, "-c");