Files
lammps/src/platform.cpp
Axel Kohlmeyer 01a54723d7 more iwyu updates
2022-12-11 23:40:31 -05:00

1089 lines
32 KiB
C++

/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/** \file platform.cpp
* This file provides abstractions for a variety of platform specific
* functionality in a namespace "platform". This is a companion to
* the "utils" namespace with convenience and utility functions. */
#include "platform.h"
#include "fmt/format.h"
#include "text_file_reader.h"
#include "utils.h"
#include <deque>
#include <exception>
#include <mpi.h>
////////////////////////////////////////////////////////////////////////
// include system headers and tweak system settings
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#if defined(_WIN32_WINNT)
#undef _WIN32_WINNT
#endif
// target Windows version is windows 7 and later
#define _WIN32_WINNT _WIN32_WINNT_WIN7
#define PSAPI_VERSION 2
#include <direct.h>
#include <io.h> // for _get_osfhandle()
#include <sys/stat.h>
#include <windows.h>
#else // not Windows ///////////////////////////////////////////////
#include <dirent.h>
#include <dlfcn.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#endif
#if defined(__APPLE__)
#include <fcntl.h>
#include <sys/syslimits.h>
#endif
////////////////////////////////////////////////////////////////////////
#include <chrono>
#include <cstring>
#include <thread>
/* ------------------------------------------------------------------ */
/// Struct for listing on-the-fly compression/decompression commands
struct compress_info {
/// identifier for the different compression algorithms
enum styles { NONE, GZIP, BZIP2, ZSTD, XZ, LZMA, LZ4 };
const std::string extension; ///< filename extension for the current algorithm
const std::string command; ///< command to perform compression or decompression
const std::string compressflags; ///< flags to append to compress from stdin to stdout
const std::string uncompressflags; ///< flags to decompress file to stdout
const int style; ///< compression style flag
};
// clang-format off
static const std::vector<compress_info> compress_styles = {
{"", "", "", "", compress_info::NONE},
{"gz", "gzip", " > ", " -cdf ", compress_info::GZIP},
{"bz2", "bzip2", " > ", " -cdf ", compress_info::BZIP2},
{"zst", "zstd", " -q > ", " -cdf ", compress_info::ZSTD},
{"xz", "xz", " > ", " -cdf ", compress_info::XZ},
{"lzma", "xz", " --format=lzma > ", " --format=lzma -cdf ", compress_info::LZMA},
{"lz4", "lz4", " > ", " -cdf ", compress_info::LZ4},
};
// clang-format on
/* ------------------------------------------------------------------ */
static const compress_info &find_compress_type(const std::string &file)
{
std::size_t dot = file.find_last_of('.');
if (dot != std::string::npos) {
const std::string ext = file.substr(dot + 1);
for (const auto &i : compress_styles) {
if (i.extension == ext) return i;
}
}
return compress_styles[0];
}
/* ------------------------------------------------------------------ */
// set reference time stamp during executable/library init.
// should provide better resolution than using epoch, if the system clock supports it.
static auto initial_time = std::chrono::steady_clock::now();
using namespace LAMMPS_NS;
// get CPU time
// clang-format off
// clang compilers are optimizing this function too aggressively returning always 0
#if defined(__clang__)
[[clang::optnone]]
#elif defined(_MSC_VER)
#pragma optimize("",off)
#endif
double platform::cputime()
// clang-format on
{
double rv = 0.0;
#ifdef _WIN32
// from MSD docs.
FILETIME ct, et, kt, ut;
union {
FILETIME ft;
uint64_t ui;
} cpu;
if (GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
cpu.ft = ut;
rv = cpu.ui * 0.0000001;
}
#else /* ! _WIN32 */
struct rusage ru;
if (getrusage(RUSAGE_SELF, &ru) == 0) {
rv = (double) ru.ru_utime.tv_sec;
rv += (double) ru.ru_utime.tv_usec * 0.000001;
}
#endif
return rv;
}
#if defined(__clang__)
#elif defined(_MSC_VER)
#pragma optimize("", on)
#endif
/* ----------------------------------------------------------------------
get wall time
------------------------------------------------------------------------ */
double platform::walltime()
{
return std::chrono::duration<double>(std::chrono::steady_clock::now() - initial_time).count();
}
/* ----------------------------------------------------------------------
sleep with microsecond resolution
------------------------------------------------------------------------ */
void platform::usleep(int usec)
{
return std::this_thread::sleep_for(std::chrono::microseconds(usec));
}
/* ----------------------------------------------------------------------
get Operating system and version info
------------------------------------------------------------------------- */
std::string platform::os_info()
{
std::string buf;
#if defined(_WIN32)
// Get Windows Edition name from registry
char value[1024];
DWORD value_length = 1024;
const char *subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
const char *entry = "CurrentBuild";
RegGetValue(HKEY_LOCAL_MACHINE, subkey, entry, RRF_RT_REG_SZ, nullptr, &value,
(LPDWORD) &value_length);
// enforce zero termination
value[1023] = '\0';
auto build = std::string(value);
if (build == "6002") {
buf = "Windows Vista";
} else if (build == "6003") {
buf = "Windows Server 2008";
} else if (build == "7601") {
buf = "Windows 7";
} else if (build == "9200") {
buf = "Windows 8";
} else if (build == "9600") {
buf = "Windows 8.1";
} else if (build == "10240") {
buf = "Windows 10 1507";
} else if (build == "10586") {
buf = "Windows 10 1511";
} else if (build == "14393") {
buf = "Windows 10 1607";
} else if (build == "15063") {
buf = "Windows 10 1703";
} else if (build == "16299") {
buf = "Windows 10 1709";
} else if (build == "17134") {
buf = "Windows 10 1803";
} else if (build == "17763") {
buf = "Windows 10 1809";
} else if (build == "18362") {
buf = "Windows 10 1903";
} else if (build == "18363") {
buf = "Windows 10 1909";
} else if (build == "19041") {
buf = "Windows 10 2004";
} else if (build == "19042") {
buf = "Windows 10 20H2";
} else if (build == "19043") {
buf = "Windows 10 21H1";
} else if (build == "19044") {
buf = "Windows 10 21H2";
} else if (build == "19045") {
buf = "Windows 10 22H2";
} else if (build == "20348") {
buf = "Windows Server 2022";
} else if (build == "22000") {
buf = "Windows 11 21H2";
} else if (build == "22621") {
buf = "Windows 11 22H2";
} else {
const char *entry = "ProductName";
RegGetValue(HKEY_LOCAL_MACHINE, subkey, entry, RRF_RT_REG_SZ, nullptr, &value,
(LPDWORD) &value_length);
// enforce zero termination
value[1023] = '\0';
buf = value;
}
DWORD fullversion, majorv, minorv, buildv = 0;
fullversion = GetVersion();
majorv = (DWORD) (LOBYTE(LOWORD(fullversion)));
minorv = (DWORD) (HIBYTE(LOWORD(fullversion)));
if (fullversion < 0x80000000) buildv = (DWORD) (HIWORD(fullversion));
buf += ", Windows ABI " + std::to_string(majorv) + "." + std::to_string(minorv) + " (" +
std::to_string(buildv) + ") on ";
SYSTEM_INFO si;
GetSystemInfo(&si);
switch (si.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64:
buf += "x86_64";
break;
case PROCESSOR_ARCHITECTURE_ARM:
buf += "arm";
break;
case PROCESSOR_ARCHITECTURE_IA64:
buf += "ia64";
break;
case PROCESSOR_ARCHITECTURE_INTEL:
buf += "i386";
break;
default:
buf += "(unknown)";
}
#else
struct utsname ut;
uname(&ut);
// try to get OS distribution name, if available
buf = ut.sysname;
if (platform::file_is_readable("/etc/os-release")) {
try {
TextFileReader reader("/etc/os-release", "");
while (true) {
auto words = reader.next_values(0, "=");
if ((words.count() > 1) && (words.next_string() == "PRETTY_NAME")) {
buf += " " + utils::trim(words.next_string());
break;
}
}
} catch (std::exception &e) {
; // EOF but keyword not found
}
}
buf += std::string(" ") + ut.release + " " + ut.machine;
#endif
return buf;
}
/* ----------------------------------------------------------------------
identify C++ standard version
------------------------------------------------------------------------- */
std::string platform::cxx_standard()
{
#if __cplusplus > 202002L
return "newer than C++20";
#elif __cplusplus == 202002L
return "C++20";
#elif __cplusplus == 201703L
return "C++17";
#elif __cplusplus == 201402L
return "C++14";
#elif __cplusplus == 201103L
return "C++11";
#elif __cplusplus == 199711L
return "C++98";
#else
return "unknown";
#endif
}
/* ----------------------------------------------------------------------
identify compiler and its version
------------------------------------------------------------------------- */
std::string platform::compiler_info()
{
std::string buf = "(Unknown)";
#if defined(__INTEL_LLVM_COMPILER)
double version = static_cast<double>(__INTEL_LLVM_COMPILER) * 0.01;
buf = fmt::format("Intel LLVM C++ {:.1f} / {}", version, __VERSION__);
#elif defined(__ibmxl__)
buf = fmt::format("IBM XL C/C++ (Clang) {}.{}.{}", __ibmxl_version__, __ibmxl_release__,
__ibmxl_modification__);
#elif defined(__clang__)
buf = fmt::format("Clang C++ {}", __VERSION__);
#elif defined(__PGI)
buf = fmt::format("PGI C++ {}.{}", __PGIC__, __PGIC_MINOR__);
#elif defined(__INTEL_COMPILER)
#if !defined(__VERSION__)
#define __VERSION__ __INTEL_COMPILER_BUILD_DATE
#endif
double version = static_cast<double>(__INTEL_COMPILER) * 0.01;
buf = fmt::format("Intel Classic C++ {:.2f}.{} / {}", version, __INTEL_COMPILER_UPDATE,
__VERSION__);
#elif defined(__MINGW64__)
buf = fmt::format("MinGW-w64 64bit {}.{} / GNU C++ {}", __MINGW64_VERSION_MAJOR,
__MINGW64_VERSION_MINOR, __VERSION__);
#elif defined(__MINGW32__)
buf = fmt::format("MinGW-w64 32bit {}.{} / GNU C++ {}", __MINGW32_MAJOR_VERSION,
__MINGW32_MINOR_VERSION, __VERSION__);
#elif defined(__GNUC__)
buf = fmt::format("GNU C++ {}", __VERSION__);
#elif defined(_MSC_VER) && (_MSC_VER >= 1920) && (_MSC_VER < 1930)
constexpr int major = _MSC_VER / 100;
constexpr int minor = _MSC_VER - major * 100;
constexpr int patch = minor - 20;
buf = fmt::format("Microsoft Visual Studio 2019 Version 16.{}, C/C++ {}.{}", patch, major - 5,
minor);
#elif defined(_MSC_VER) && (_MSC_VER >= 1930) && (_MSC_VER < 2000)
constexpr int major = _MSC_VER / 100;
constexpr int minor = _MSC_VER - major * 100;
constexpr int patch = minor - 30;
buf = fmt::format("Microsoft Visual Studio 2022 Version 17.{}, C/C++ {}.{}", patch, major - 5,
minor);
#else
buf = "(Unknown)";
#endif
return buf;
}
/* ----------------------------------------------------------------------
detect OpenMP standard
------------------------------------------------------------------------- */
std::string platform::openmp_standard()
{
#if !defined(_OPENMP)
return "OpenMP not enabled";
#else
// Supported OpenMP version corresponds to the release date of the
// specifications as posted at https://www.openmp.org/specifications/
#if _OPENMP > 202011
return "OpenMP newer than version 5.1";
#elif _OPENMP == 202011
return "OpenMP 5.1";
#elif _OPENMP == 201811
return "OpenMP 5.0";
#elif _OPENMP == 201611
return "OpenMP 5.0 preview 1";
#elif _OPENMP == 201511
return "OpenMP 4.5";
#elif _OPENMP == 201307
return "OpenMP 4.0";
#elif _OPENMP == 201107
return "OpenMP 3.1";
#elif _OPENMP == 200805
return "OpenMP 3.0";
#elif _OPENMP == 200505
return "OpenMP 2.5";
#elif _OPENMP == 200203
return "OpenMP 2.0";
#else
return "unknown OpenMP version";
#endif
#endif
}
/* ----------------------------------------------------------------------
identify MPI vendor from defines in the mpi.h file.
------------------------------------------------------------------------- */
std::string platform::mpi_vendor()
{
#if defined(MPI_STUBS)
return "MPI STUBS";
#elif defined(OPEN_MPI)
return "Open MPI";
#elif defined(MPICH_NAME)
return "MPICH";
#elif defined(I_MPI_VERSION)
return "Intel MPI";
#elif defined(PLATFORM_MPI)
return "Platform MPI";
#elif defined(HP_MPI)
return "HP MPI";
#elif defined(MSMPI_VER)
// Get Microsoft MPI version from registry
char value[1024];
DWORD value_length = 1024;
const char *subkey = "SOFTWARE\\Microsoft\\MPI";
const char *entry = "Version";
auto rv = RegGetValueA(HKEY_LOCAL_MACHINE, subkey, entry, RRF_RT_REG_SZ, nullptr, &value,
(LPDWORD) &value_length);
std::string buf = "Microsoft MPI";
if (rv == ERROR_SUCCESS) buf += std::string(" v") + value;
return buf;
#else
return "Unknown MPI implementation";
#endif
}
/* ----------------------------------------------------------------------
detect MPI version info
------------------------------------------------------------------------- */
std::string platform::mpi_info(int &major, int &minor)
{
#if (defined(MPI_VERSION) && (MPI_VERSION > 2)) || defined(MPI_STUBS)
int len = 0;
static char version[MPI_MAX_LIBRARY_VERSION_STRING];
MPI_Get_library_version(version, &len);
if (len > 80) {
char *ptr = strchr(version + 80, '\n');
if (ptr) *ptr = '\0';
}
#else
constexpr int MAX_VERSION_STRING = 32;
static char version[MAX_VERSION_STRING];
strncpy(version, mpi_vendor().c_str(), MAX_VERSION_STRING);
#endif
#if defined(MPI_VERSION)
MPI_Get_version(&major, &minor);
#else
major = 1;
minor = 0;
#endif
return {version};
}
/* ----------------------------------------------------------------------
collect available compression tool info
------------------------------------------------------------------------- */
std::string platform::compress_info()
{
std::string buf = "Available compression formats:\n\n";
bool none_found = true;
for (const auto &cmpi : compress_styles) {
if (cmpi.style == ::compress_info::NONE) continue;
if (find_exe_path(cmpi.command).size()) {
none_found = false;
buf += fmt::format("Extension: .{:6} Command: {}\n", cmpi.extension, cmpi.command);
}
}
if (none_found) buf += "None\n";
return buf;
}
/* ----------------------------------------------------------------------
set environment variable
------------------------------------------------------------------------- */
int platform::putenv(const std::string &vardef)
{
if (vardef.size() == 0) return -1;
auto found = vardef.find_first_of('=');
#ifdef _WIN32
// must assign a value to variable with _putenv_s()
if (found == std::string::npos)
return _putenv_s(vardef.c_str(), "1");
else
return _putenv_s(vardef.substr(0, found).c_str(), vardef.substr(found + 1).c_str());
#else
if (found == std::string::npos)
return setenv(vardef.c_str(), "", 1);
else
return setenv(vardef.substr(0, found).c_str(), vardef.substr(found + 1).c_str(), 1);
#endif
return -1;
}
/* ----------------------------------------------------------------------
unset environment variable
------------------------------------------------------------------------- */
int platform::unsetenv(const std::string &variable)
{
if (variable.size() == 0) return -1;
#ifdef _WIN32
// emulate POSIX semantics by returning -1 on trying to unset non-existing variable
const char *ptr = getenv(variable.c_str());
if (!ptr) return -1;
// empty _putenv_s() definition deletes variable
return _putenv_s(variable.c_str(), "");
#else
return ::unsetenv(variable.c_str());
#endif
}
/* ----------------------------------------------------------------------
split a "path" environment variable into a list
------------------------------------------------------------------------- */
std::vector<std::string> platform::list_pathenv(const std::string &var)
{
std::vector<std::string> dirs;
const char *ptr = getenv(var.c_str());
if (ptr == nullptr) return dirs;
std::string pathvar = ptr;
std::size_t first = 0, next;
while (true) {
next = pathvar.find_first_of(pathvarsep, first);
if (next == std::string::npos) {
dirs.push_back(pathvar.substr(first));
break;
} else {
dirs.push_back(pathvar.substr(first, next - first));
first = next + 1;
}
}
return dirs;
}
/* ----------------------------------------------------------------------
find the full path name of an executable
------------------------------------------------------------------------- */
std::string platform::find_exe_path(const std::string &cmd)
{
if (cmd.size() == 0) return "";
auto pathdirs = list_pathenv("PATH");
#ifdef _WIN32
// windows always looks in "." and does it first
pathdirs.insert(pathdirs.begin(), ".");
#else
struct stat info;
#endif
for (const auto &dir : pathdirs) {
std::string exe = path_join(dir, cmd);
#ifdef _WIN32
const char *extensions[] = {".exe", ".com", ".bat", nullptr};
for (auto ext = extensions; *ext != nullptr; ++ext) {
auto exe_path = exe + *ext;
if (file_is_readable(exe_path)) return exe_path;
}
#else
memset(&info, 0, sizeof(info));
if (stat(exe.c_str(), &info) != 0) continue;
if ((info.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) != 0) return exe;
#endif
}
return "";
}
/* ----------------------------------------------------------------------
wrapper functions for loading shared objects and libraries
------------------------------------------------------------------------- */
#ifdef _WIN32
// open a shared object file
void *platform::dlopen(const std::string &fname)
{
return (void *) LoadLibrary(fname.c_str());
}
// return dynamic linker error string
std::string platform::dlerror()
{
return "";
}
// close a shared object
int platform::dlclose(void *handle)
{
/* FreeLibrary returns nonzero on success unlike dlclose() */
return (FreeLibrary((HINSTANCE) handle) == 0);
}
// resolve a symbol in shared object
void *platform::dlsym(void *handle, const std::string &symbol)
{
return (void *) GetProcAddress((HINSTANCE) handle, symbol.c_str());
}
#else
// open a shared object file
void *platform::dlopen(const std::string &fname)
{
return ::dlopen(fname.c_str(), RTLD_NOW | RTLD_GLOBAL);
}
// return dynamic linker error string
std::string platform::dlerror()
{
const char *errmesg = ::dlerror();
if (errmesg)
return {errmesg};
else
return {""};
}
// close a shared object
int platform::dlclose(void *handle)
{
return ::dlclose(handle);
}
// resolve a symbol in shared object
void *platform::dlsym(void *handle, const std::string &symbol)
{
return ::dlsym(handle, symbol.c_str());
}
#endif
/* ---------------------------------------------------------------------- */
/** On Linux the folder /proc/self/fd holds symbolic links to the actual
* pathnames associated with each open file descriptor of the current process.
* On MacOS the same kind of information can be obtained using ``fcntl(fd,F_GETPATH,buf)``.
* On Windows we use ``GetFinalPathNameByHandleA()`` which is available with
* Windows Vista and later. If the buffer is too small (< 16 bytes) a null pointer is returned.
*
* This function is used to provide a filename with error messages in functions
* where the filename is not passed as an argument, but the FILE * pointer. */
const char *platform::guesspath(FILE *fp, char *buf, int len)
{
// no point in guessing a path with a short buffer or NULL pointer as buffer
if ((buf == nullptr) || (len < 16)) return nullptr;
// zero buffer and reserve last character in buffer for terminating '\0'
memset(buf, 0, len);
len--;
#if defined(__linux__)
int fd = fileno(fp);
// get pathname from /proc or copy (unknown)
if (readlink((std::string("/proc/self/fd/") + std::to_string(fd)).c_str(), buf, len) <= 0)
strncpy(buf, "(unknown)", len);
#elif defined(__APPLE__)
int fd = fileno(fp);
char filepath[PATH_MAX];
if (fcntl(fd, F_GETPATH, filepath) != -1)
strncpy(buf, filepath, len);
else
strncpy(buf, "(unknown)", len);
#elif defined(_WIN32)
char filepath[MAX_PATH];
HANDLE h = (HANDLE) _get_osfhandle(_fileno(fp));
if (GetFinalPathNameByHandleA(h, filepath, MAX_PATH, FILE_NAME_NORMALIZED) > 0)
strncpy(buf, filepath, len);
else
strncpy(buf, "(unknown)", len);
#else // unsupported OS
strncpy(buf, "(unknown)", len);
#endif
return buf;
}
/* ----------------------------------------------------------------------
detect terminal, e.g. for using a pager automatically
------------------------------------------------------------------------- */
bool platform::is_console(FILE *fp)
{
if (!fp) return false;
#if defined(_WIN32)
return (_isatty(_fileno(fp)) == 1);
#else
return (isatty(fileno(fp)) == 1);
#endif
}
/* ----------------------------------------------------------------------
Get string with path to the current directory
PATH_MAX may not be a compile time constant, so we must allocate and delete a buffer.
------------------------------------------------------------------------- */
std::string platform::current_directory()
{
std::string cwd;
#if defined(_WIN32)
char *buf = new char[MAX_PATH];
if (_getcwd(buf, MAX_PATH)) { cwd = buf; }
delete[] buf;
#else
auto buf = new char[PATH_MAX];
if (::getcwd(buf, PATH_MAX)) { cwd = buf; }
delete[] buf;
#endif
return cwd;
}
/* ----------------------------------------------------------------------
check if a path is a directory
------------------------------------------------------------------------- */
bool platform::path_is_directory(const std::string &path)
{
#if defined(_WIN32)
struct _stat info;
memset(&info, 0, sizeof(info));
if (_stat(path.c_str(), &info) != 0) return false;
#else
struct stat info;
memset(&info, 0, sizeof(info));
if (stat(path.c_str(), &info) != 0) return false;
#endif
return ((info.st_mode & S_IFDIR) != 0);
}
/* ----------------------------------------------------------------------
get directory listing in string vector
------------------------------------------------------------------------- */
std::vector<std::string> platform::list_directory(const std::string &dir)
{
std::vector<std::string> files;
if (!path_is_directory(dir)) return files;
#if defined(_WIN32)
HANDLE handle;
WIN32_FIND_DATA fd;
std::string searchname = dir + filepathsep[0] + "*";
handle = FindFirstFile(searchname.c_str(), &fd);
if (handle == ((HANDLE) -1)) return files;
while (FindNextFile(handle, &fd)) {
std::string entry(fd.cFileName);
if ((entry == "..") || (entry == ".")) continue;
files.push_back(entry);
}
FindClose(handle);
#else
std::string dirname = dir + filepathsep[0];
DIR *handle = opendir(dirname.c_str());
if (handle == nullptr) return files;
struct dirent *fd;
while ((fd = readdir(handle)) != nullptr) {
std::string entry(fd->d_name);
if ((entry == "..") || (entry == ".")) continue;
files.push_back(entry);
}
closedir(handle);
#endif
return files;
}
/* ----------------------------------------------------------------------
Change current directory
------------------------------------------------------------------------- */
int platform::chdir(const std::string &path)
{
#if defined(_WIN32)
return ::_chdir(path.c_str());
#else
return ::chdir(path.c_str());
#endif
}
/* ----------------------------------------------------------------------
Create a directory. Create entire path if necessary.
------------------------------------------------------------------------- */
int platform::mkdir(const std::string &path)
{
std::deque<std::string> dirlist = {path};
std::string dirname = path_dirname(path);
while ((dirname != ".") && (dirname != "")) {
dirlist.push_front(dirname);
dirname = path_dirname(dirname);
}
int rv;
for (const auto &dir : dirlist) {
if (!path_is_directory(dir)) {
#if defined(_WIN32)
rv = ::_mkdir(dir.c_str());
#else
rv = ::mkdir(dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP);
#endif
if (rv != 0) return rv;
}
}
return 0;
}
/* ----------------------------------------------------------------------
Delete a directory and its contents recursively
------------------------------------------------------------------------- */
int platform::rmdir(const std::string &path)
{
// recurse through directory tree deleting files and directories
auto entries = list_directory(path);
for (const auto &entry : entries) {
const auto newpath = path_join(path, entry);
if (path_is_directory(newpath))
rmdir(newpath);
else
unlink(newpath);
}
#if defined(_WIN32)
return ::_rmdir(path.c_str());
#else
return ::rmdir(path.c_str());
#endif
}
/* ----------------------------------------------------------------------
Delete a file
------------------------------------------------------------------------- */
int platform::unlink(const std::string &path)
{
#if defined(_WIN32)
return ::_unlink(path.c_str());
#else
return ::unlink(path.c_str());
#endif
}
/* ----------------------------------------------------------------------
Get current file stream position
------------------------------------------------------------------------- */
bigint platform::ftell(FILE *fp)
{
#if defined(_WIN32)
return (bigint)::_ftelli64(fp);
#else
return (bigint)::ftell(fp);
#endif
}
/* ----------------------------------------------------------------------
Set current file stream position
------------------------------------------------------------------------- */
int platform::fseek(FILE *fp, bigint pos)
{
#if defined(_WIN32)
if (pos == platform::END_OF_FILE)
return ::_fseeki64(fp, 0, SEEK_END);
else
return ::_fseeki64(fp, (__int64) pos, SEEK_SET);
#else
if (pos == platform::END_OF_FILE)
return ::fseek(fp, 0, SEEK_END);
else
return ::fseek(fp, (long) pos, SEEK_SET);
#endif
}
/* ----------------------------------------------------------------------
Truncate opened file to given length
------------------------------------------------------------------------- */
int platform::ftruncate(FILE *fp, bigint length)
{
#if defined(_WIN32)
HANDLE h = (HANDLE) _get_osfhandle(_fileno(fp));
LARGE_INTEGER li_start, li_length;
li_start.QuadPart = (int64_t) 0;
li_length.QuadPart = (int64_t) length;
if (SetFilePointerEx(h, li_start, NULL, FILE_CURRENT) &&
SetFilePointerEx(h, li_length, NULL, FILE_BEGIN) && SetEndOfFile(h)) {
return 0;
} else {
return 1;
}
#else
platform::fseek(fp, length);
return ::ftruncate(fileno(fp), (off_t) length);
#endif
}
/* ----------------------------------------------------------------------
open pipe
------------------------------------------------------------------------- */
FILE *platform::popen(const std::string &cmd, const std::string &mode)
{
FILE *fp = nullptr;
#if defined(_WIN32)
if (mode == "r")
fp = ::_popen(cmd.c_str(), "rb");
else if (mode == "w")
fp = ::_popen(cmd.c_str(), "wb");
#else
if (mode == "r")
fp = ::popen(cmd.c_str(), "r");
else if (mode == "w")
fp = ::popen(cmd.c_str(), "w");
#endif
return fp;
}
/* ----------------------------------------------------------------------
close pipe
------------------------------------------------------------------------- */
int platform::pclose(FILE *fp)
{
#if defined(_WIN32)
return ::_pclose(fp);
#else
return ::pclose(fp);
#endif
}
/* ----------------------------------------------------------------------
strip off leading part of path, return just the filename
------------------------------------------------------------------------- */
std::string platform::path_basename(const std::string &path)
{
size_t start = path.find_last_of(platform::filepathsep);
if (start == std::string::npos) {
start = 0;
} else {
start += 1;
}
return path.substr(start);
}
/* ----------------------------------------------------------------------
Return only the leading part of a path, return just the directory
------------------------------------------------------------------------- */
std::string platform::path_dirname(const std::string &path)
{
size_t start = path.find_last_of(platform::filepathsep);
if (start == std::string::npos) return ".";
return path.substr(0, start);
}
/* ----------------------------------------------------------------------
join two paths.
if one of the two is an empty string just return the other unmodified
if the first string ends in the separator or the second begins with one, trim them
------------------------------------------------------------------------- */
std::string platform::path_join(const std::string &a, const std::string &b)
{
if (a.empty()) return b;
if (b.empty()) return a;
// remove trailing separator(s) in first part
std::string joined = a;
while (joined.find_last_of(platform::filepathsep) == joined.size() - 1) {
for (const auto &s : platform::filepathsep)
if (joined.back() == s) joined.pop_back();
}
// skip over leading separator(s) in second part
std::size_t skip = 0;
while (b.find_first_of(platform::filepathsep, skip) == skip) ++skip;
// combine and return
joined += platform::filepathsep[0] + b.substr(skip);
return joined;
}
/* ----------------------------------------------------------------------
try to open file for reading to prove if it exists and is accessible
------------------------------------------------------------------------- */
bool platform::file_is_readable(const std::string &path)
{
FILE *fp = fopen(path.c_str(), "r");
if (fp) {
fclose(fp);
return true;
}
return false;
}
/* ----------------------------------------------------------------------
check if filename has a known compression extension
------------------------------------------------------------------------- */
bool platform::has_compress_extension(const std::string &file)
{
return find_compress_type(file).style != ::compress_info::NONE;
}
/* ----------------------------------------------------------------------
open pipe to read a compressed file
------------------------------------------------------------------------- */
FILE *platform::compressed_read(const std::string &file)
{
FILE *fp = nullptr;
#if defined(LAMMPS_GZIP)
auto compress = find_compress_type(file);
if (compress.style == ::compress_info::NONE) return nullptr;
if (find_exe_path(compress.command).size())
// put quotes around file name so that they may contain blanks
fp = popen((compress.command + compress.uncompressflags + "\"" + file + "\""), "r");
#endif
return fp;
}
/* ----------------------------------------------------------------------
open pipe to write a compressed file
------------------------------------------------------------------------- */
FILE *platform::compressed_write(const std::string &file)
{
FILE *fp = nullptr;
#if defined(LAMMPS_GZIP)
auto compress = find_compress_type(file);
if (compress.style == ::compress_info::NONE) return nullptr;
if (find_exe_path(compress.command).size())
// put quotes around file name so that they may contain blanks
fp = popen((compress.command + compress.compressflags + "\"" + file + "\""), "w");
#endif
return fp;
}
/* ---------------------------------------------------------------------- */