mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2025-01-02 11:30:04 +00:00
move cxx stuff into cxx directory
This commit is contained in:
parent
9316311a1d
commit
446e3e5e1e
@ -114,8 +114,13 @@ add_custom_command(
|
||||
# mpw-shell-execute.cpp mpw-shell-builtins.cpp mpw-shell-read.cpp
|
||||
add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
||||
mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
|
||||
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp mapped_file.cpp
|
||||
pathnames.cpp)
|
||||
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp
|
||||
pathnames.cpp
|
||||
cxx/mapped_file.cpp
|
||||
cxx/filesystem.cpp
|
||||
cxx/path.cpp
|
||||
cxx/directory_iterator.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(mpw-shell edit)
|
||||
|
||||
|
193
cxx/directory_iterator.cpp
Normal file
193
cxx/directory_iterator.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
#include "filesystem.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
|
||||
namespace filesystem {
|
||||
|
||||
|
||||
#pragma mark - directory_entry
|
||||
|
||||
directory_entry::directory_entry(const class path& p, file_status st, file_status symlink_st) :
|
||||
_path(p), _st(st), _lst(symlink_st)
|
||||
{}
|
||||
|
||||
void directory_entry::assign(const class path& p, file_status st, file_status symlink_st)
|
||||
{
|
||||
_path = p;
|
||||
_st = st;
|
||||
_lst = symlink_st;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void directory_entry::replace_filename(const path& p, file_status st, file_status symlink_st)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
file_status directory_entry::status() const {
|
||||
|
||||
if (!status_known(_st))
|
||||
{
|
||||
if (status_known(_lst) && ! is_symlink(_lst)) {
|
||||
_st = _lst;
|
||||
} else {
|
||||
_st = filesystem::status(_path);
|
||||
}
|
||||
}
|
||||
|
||||
return _st;
|
||||
}
|
||||
|
||||
file_status directory_entry::status(error_code& ec) const noexcept {
|
||||
|
||||
if (!status_known(_st))
|
||||
{
|
||||
if (status_known(_lst) && ! is_symlink(_lst)) {
|
||||
_st = _lst;
|
||||
} else {
|
||||
_st = filesystem::status(_path, ec);
|
||||
}
|
||||
}
|
||||
|
||||
return _st;
|
||||
}
|
||||
|
||||
file_status directory_entry::symlink_status() const {
|
||||
|
||||
if (!status_known(_lst))
|
||||
_lst = filesystem::symlink_status(_path);
|
||||
|
||||
return _lst;
|
||||
}
|
||||
|
||||
file_status directory_entry::symlink_status(error_code& ec) const noexcept {
|
||||
|
||||
ec.clear();
|
||||
|
||||
if (!status_known(_lst))
|
||||
_lst = filesystem::symlink_status(_path, ec);
|
||||
|
||||
return _lst;
|
||||
}
|
||||
|
||||
|
||||
bool directory_entry::operator< (const directory_entry& rhs) const noexcept {
|
||||
return _path < rhs._path;
|
||||
}
|
||||
bool directory_entry::operator==(const directory_entry& rhs) const noexcept {
|
||||
return _path == rhs._path;
|
||||
}
|
||||
bool directory_entry::operator!=(const directory_entry& rhs) const noexcept {
|
||||
return _path != rhs._path;
|
||||
}
|
||||
bool directory_entry::operator<=(const directory_entry& rhs) const noexcept {
|
||||
return _path <= rhs._path;
|
||||
}
|
||||
bool directory_entry::operator> (const directory_entry& rhs) const noexcept {
|
||||
return _path > rhs._path;
|
||||
}
|
||||
bool directory_entry::operator>=(const directory_entry& rhs) const noexcept {
|
||||
return _path >= rhs._path;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - directory_iterator
|
||||
|
||||
directory_iterator::directory_iterator(const path& p) {
|
||||
|
||||
|
||||
error_code ec;
|
||||
|
||||
open_dir(p, ec);
|
||||
if (ec) throw filesystem_error("directory_iterator::directory_iterator", p, ec);
|
||||
|
||||
increment(ec);
|
||||
if (ec) throw filesystem_error("directory_iterator::directory_iterator", p, ec);
|
||||
}
|
||||
|
||||
directory_iterator::directory_iterator(const path& p, error_code& ec) noexcept {
|
||||
ec.clear();
|
||||
|
||||
open_dir(p, ec);
|
||||
if (!ec) increment(ec);
|
||||
}
|
||||
|
||||
void directory_iterator::open_dir(const path &p, error_code &ec) noexcept {
|
||||
|
||||
DIR *dp = opendir(p.c_str());
|
||||
if (!dp)
|
||||
{
|
||||
ec = error_code(errno, std::system_category());
|
||||
return;
|
||||
}
|
||||
|
||||
_imp = std::make_shared<imp>(p, dp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
directory_iterator& directory_iterator::operator++() {
|
||||
|
||||
error_code ec;
|
||||
|
||||
increment(ec);
|
||||
if (ec) throw filesystem_error("directory_iterator::operator++", ec);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
directory_iterator& directory_iterator::increment(error_code& ec) noexcept {
|
||||
|
||||
ec.clear();
|
||||
|
||||
if (_imp) {
|
||||
|
||||
struct dirent entry;
|
||||
struct dirent *result = nullptr;
|
||||
int rv;
|
||||
|
||||
DIR *dp = _imp->_dp;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
|
||||
|
||||
rv = readdir_r(dp, &entry, &result);
|
||||
if (rv != 0)
|
||||
{
|
||||
ec = error_code(errno, std::system_category());
|
||||
_imp.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
_imp.reset();
|
||||
break; // end of directory.
|
||||
}
|
||||
|
||||
std::string s(entry.d_name);
|
||||
if (s == ".") continue;
|
||||
if (s == "..") continue;
|
||||
|
||||
|
||||
path p = _imp->_path / s;
|
||||
|
||||
_imp->_entry.assign(p);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
289
cxx/filesystem.cpp
Normal file
289
cxx/filesystem.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
#include "filesystem.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
namespace filesystem {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
template<class FX, class... Args>
|
||||
auto syscall(error_code &ec, FX fx, Args&&... args) -> decltype(fx(std::forward<Args>(args)...))
|
||||
{
|
||||
auto rv = fx(std::forward<Args>(args)...);
|
||||
if (rv < 0) {
|
||||
ec = error_code(errno, std::system_category());
|
||||
} else {
|
||||
ec.clear();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int fs_stat(const path &p, struct stat *buf, error_code &ec) {
|
||||
int rv = stat(p.c_str(), buf);
|
||||
if (rv < 0) {
|
||||
ec = error_code(errno, std::system_category());
|
||||
}
|
||||
else {
|
||||
ec.clear();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int fs_lstat(const path &p, struct stat *buf, error_code &ec) {
|
||||
int rv = lstat(p.c_str(), buf);
|
||||
if (rv < 0) {
|
||||
ec = error_code(errno, std::system_category());
|
||||
}
|
||||
else {
|
||||
ec.clear();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
template<class FX>
|
||||
file_status status_common(FX fx, const path& p, error_code& ec) noexcept {
|
||||
|
||||
|
||||
struct stat st;
|
||||
int rv = fx(p, &st, ec);
|
||||
if (rv < 0)
|
||||
{
|
||||
switch (ec.value())
|
||||
{
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
return file_status(file_type::not_found);
|
||||
|
||||
case EOVERFLOW:
|
||||
return file_status(file_type::unknown);
|
||||
|
||||
//case ENAMETOOLONG: ???
|
||||
// case ELOOP ?
|
||||
|
||||
default:
|
||||
return file_status(file_type::none);
|
||||
}
|
||||
}
|
||||
|
||||
ec.clear();
|
||||
perms prms = static_cast<perms>(st.st_mode & perms::mask);
|
||||
|
||||
if (S_ISREG(st.st_mode))
|
||||
return file_status(file_type::regular, prms);
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return file_status(file_type::directory, prms);
|
||||
|
||||
if (S_ISBLK(st.st_mode))
|
||||
return file_status(file_type::block, prms);
|
||||
|
||||
if (S_ISFIFO(st.st_mode))
|
||||
return file_status(file_type::fifo, prms);
|
||||
|
||||
if (S_ISSOCK(st.st_mode))
|
||||
return file_status(file_type::socket, prms);
|
||||
|
||||
return file_status(file_type::unknown, prms);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
file_status status(const path& p) {
|
||||
error_code ec;
|
||||
file_status result = status(p, ec);
|
||||
if (result.type() == file_type::none)
|
||||
throw filesystem_error("filesystem::file_status", p, ec);
|
||||
return result;
|
||||
}
|
||||
|
||||
file_status status(const path& p, error_code& ec) noexcept {
|
||||
|
||||
return status_common(fs_stat, p, ec);
|
||||
/*
|
||||
struct stat st;
|
||||
int rv = stat(p.c_str(), &st);
|
||||
if (rv < 0) {
|
||||
int e = errno;
|
||||
ec = error_code(e, std::system_category());
|
||||
|
||||
switch(e){
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
return file_status(file_type::not_found);
|
||||
|
||||
case EOVERFLOW:
|
||||
return file_status(file_type::unknown);
|
||||
|
||||
//case ENAMETOOLONG: ???
|
||||
// case ELOOP ?
|
||||
|
||||
default:
|
||||
return file_status(file_type::none);
|
||||
}
|
||||
}
|
||||
|
||||
ec.clear();
|
||||
perms prms = static_cast<perms>(st.st_mode & perms::mask);
|
||||
|
||||
if (S_ISREG(st.st_mode))
|
||||
return file_status(file_type::regular, prms);
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return file_status(file_type::directory, prms);
|
||||
|
||||
if (S_ISBLK(st.st_mode))
|
||||
return file_status(file_type::block, prms);
|
||||
|
||||
if (S_ISFIFO(st.st_mode))
|
||||
return file_status(file_type::fifo, prms);
|
||||
|
||||
if (S_ISSOCK(st.st_mode))
|
||||
return file_status(file_type::socket, prms);
|
||||
|
||||
return file_status(file_type::unknown, prms);
|
||||
*/
|
||||
}
|
||||
|
||||
file_status symlink_status(const path& p) {
|
||||
error_code ec;
|
||||
file_status result = symlink_status(p, ec);
|
||||
if (result.type() == file_type::none)
|
||||
throw filesystem_error("filesystem::symlink_status", p, ec);
|
||||
return result;
|
||||
}
|
||||
|
||||
file_status symlink_status(const path& p, error_code& ec) noexcept {
|
||||
|
||||
return status_common(fs_lstat, p, ec);
|
||||
}
|
||||
|
||||
|
||||
uintmax_t file_size(const path& p) {
|
||||
error_code ec;
|
||||
|
||||
struct stat st;
|
||||
//if (fs_stat(p, &st, ec) < 0)
|
||||
if (syscall(ec, ::stat, p.c_str(), &st) < 0)
|
||||
throw filesystem_error("filesystem::file_size", p, ec);
|
||||
|
||||
return st.st_size;
|
||||
|
||||
}
|
||||
|
||||
uintmax_t file_size(const path& p, error_code& ec) noexcept {
|
||||
|
||||
struct stat st;
|
||||
|
||||
//if (fs_stat(p, &st, ec) < 0)
|
||||
if (syscall(ec, ::stat, p.c_str(), &st) < 0)
|
||||
return static_cast<uintmax_t>(-1);
|
||||
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool create_directory(const path& p) {
|
||||
error_code ec;
|
||||
bool rv = create_directory(p, ec);
|
||||
if (ec)
|
||||
throw filesystem_error("filesystem::create_directory", p, ec);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool create_directory(const path& p, error_code& ec) noexcept {
|
||||
|
||||
int rv = ::mkdir(p.c_str(), S_IRWXU|S_IRWXG|S_IRWXO);
|
||||
if (rv == 0) {
|
||||
ec.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
int e = errno;
|
||||
error_code tmp;
|
||||
|
||||
// special case -- not an error if the directory already exists.
|
||||
|
||||
if (e == EEXIST && is_directory(p, tmp)) {
|
||||
ec.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
ec = error_code(e, std::system_category());
|
||||
return false;
|
||||
}
|
||||
|
||||
void resize_file(const path& p, uintmax_t new_size) {
|
||||
error_code ec;
|
||||
|
||||
if (syscall(ec, ::truncate, p.c_str(), new_size) < 0)
|
||||
throw filesystem_error("filesystem::create_directory", p, ec);
|
||||
}
|
||||
|
||||
void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept {
|
||||
|
||||
syscall(ec, ::truncate, p.c_str(), new_size);
|
||||
|
||||
}
|
||||
|
||||
bool remove(const path& p) {
|
||||
error_code ec;
|
||||
|
||||
if (syscall(ec, ::remove, p.c_str()) < 0)
|
||||
{
|
||||
throw filesystem_error("filesystem::remove", p, ec);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(const path& p, error_code& ec) noexcept {
|
||||
if (syscall(ec, ::remove, p.c_str()) < 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
path current_path() {
|
||||
error_code ec;
|
||||
path p = current_path(ec);
|
||||
|
||||
if (ec)
|
||||
throw filesystem_error("filesystem::current_path", ec);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
path current_path(error_code& ec) {
|
||||
|
||||
char *cp;
|
||||
char buffer[PATH_MAX];
|
||||
|
||||
ec.clear();
|
||||
cp = ::getcwd(buffer, sizeof(buffer));
|
||||
if (cp) return path(cp);
|
||||
|
||||
ec = error_code(errno, std::system_category());
|
||||
return path();
|
||||
}
|
||||
|
||||
void current_path(const path& p) {
|
||||
error_code ec;
|
||||
syscall(ec, ::chdir, p.c_str());
|
||||
if (ec)
|
||||
throw filesystem_error("filesystem::current_path", p, ec);
|
||||
}
|
||||
|
||||
void current_path(const path& p, error_code& ec) noexcept {
|
||||
|
||||
syscall(ec, ::chdir, p.c_str());
|
||||
}
|
||||
|
||||
}
|
848
cxx/filesystem.h
Normal file
848
cxx/filesystem.h
Normal file
@ -0,0 +1,848 @@
|
||||
#ifndef __filesystem_h__
|
||||
#define __filesystem_h__
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <locale>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <memory>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
/*
|
||||
*
|
||||
* light-weight implementation of n3803
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace filesystem {
|
||||
|
||||
class path;
|
||||
class filesystem_error;
|
||||
|
||||
using std::error_code;
|
||||
using std::system_error;
|
||||
|
||||
class path {
|
||||
|
||||
public:
|
||||
|
||||
typedef char value_type;
|
||||
typedef std::basic_string<value_type> string_type;
|
||||
static constexpr value_type preferred_separator = '/';
|
||||
|
||||
|
||||
// constructors and destructor
|
||||
path() = default;
|
||||
path(const path& p) = default;
|
||||
path(path&& p) noexcept :
|
||||
_path(std::move(p._path)) {
|
||||
p.invalidate();
|
||||
}
|
||||
|
||||
template<class Source>
|
||||
path(Source const& source) :
|
||||
_path(source) {}
|
||||
|
||||
template <class InputIterator>
|
||||
path(InputIterator begin, InputIterator end) :
|
||||
_path(begin, end) {}
|
||||
|
||||
template <class Source>
|
||||
path(Source const& source, const std::locale& loc);
|
||||
|
||||
template <class InputIterator>
|
||||
path(InputIterator begin, InputIterator end, const std::locale& loc);
|
||||
|
||||
~path() = default;
|
||||
|
||||
// assignments
|
||||
path& operator=(const path& p) {
|
||||
if (this != &p) {
|
||||
_path = p._path;
|
||||
_info = p._info;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
path& operator=(path&& p) noexcept {
|
||||
if (this != &p) {
|
||||
_path = std::move(p._path);
|
||||
_info = p._info;
|
||||
p.invalidate();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Source>
|
||||
path& operator=(Source const& source) {
|
||||
invalidate();
|
||||
_path = source;
|
||||
return *this;
|
||||
}
|
||||
|
||||
path& assign(const path &p) {
|
||||
return (*this = p);
|
||||
}
|
||||
|
||||
template <class Source>
|
||||
path& assign(Source const& source) {
|
||||
invalidate();
|
||||
_path.assign(source);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
path& assign(InputIterator begin, InputIterator end) {
|
||||
invalidate();
|
||||
_path.assign(begin, end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// appends
|
||||
path& operator/=(const path& p) {
|
||||
return append(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class Source>
|
||||
path& operator/=(Source const& source) {
|
||||
return append(source);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// consider this a specialization of append.
|
||||
path& append(const path &p);
|
||||
|
||||
path& append(const string_type &s);
|
||||
|
||||
|
||||
template <class Source>
|
||||
path& append(Source const& source) {
|
||||
return append(path(source));
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
path& append(InputIterator begin, InputIterator end) {
|
||||
return append(path(begin, end));
|
||||
}
|
||||
|
||||
|
||||
// concatenation
|
||||
path& operator+=(const path& x) {
|
||||
invalidate();
|
||||
_path += x._path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
path& operator+=(const string_type& x) {
|
||||
invalidate();
|
||||
_path += x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
path& operator+=(const value_type* x) {
|
||||
invalidate();
|
||||
_path += x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
path& operator+=(value_type x) {
|
||||
invalidate();
|
||||
_path += x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Source>
|
||||
path& operator+=(Source const& x) {
|
||||
invalidate();
|
||||
_path += x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class charT>
|
||||
path& operator+=(charT x){
|
||||
invalidate();
|
||||
_path += x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Source>
|
||||
path& concat(Source const& x) {
|
||||
invalidate();
|
||||
_path += x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
path& concat(InputIterator begin, InputIterator end) {
|
||||
invalidate();
|
||||
_path.append(begin, end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// modifiers
|
||||
void clear() noexcept {
|
||||
invalidate();
|
||||
_path.clear();
|
||||
}
|
||||
|
||||
path& make_preferred() {
|
||||
return *this;
|
||||
}
|
||||
path& remove_filename();
|
||||
path& replace_filename(const path& replacement);
|
||||
path& replace_extension(const path& replacement = path());
|
||||
|
||||
void swap(path& rhs) noexcept {
|
||||
std::swap(_path, rhs._path);
|
||||
std::swap(_info, rhs._info);
|
||||
}
|
||||
|
||||
// native format observers
|
||||
const string_type& native() const noexcept {
|
||||
return _path;
|
||||
}
|
||||
|
||||
const value_type* c_str() const noexcept {
|
||||
return _path.c_str();
|
||||
}
|
||||
|
||||
operator string_type() const {
|
||||
return _path;
|
||||
}
|
||||
|
||||
template <class charT, class traits = std::char_traits<charT>,
|
||||
class Allocator = std::allocator<charT> >
|
||||
std::basic_string<charT, traits, Allocator> string(const Allocator& a = Allocator()) const;
|
||||
|
||||
std::string string() const {
|
||||
return _path;
|
||||
}
|
||||
|
||||
std::wstring wstring() const;
|
||||
std::string u8string() const;
|
||||
std::u16string u16string() const;
|
||||
std::u32string u32string() const;
|
||||
|
||||
// generic format observers
|
||||
template <class charT, class traits = std::char_traits<charT>,
|
||||
class Allocator = std::allocator<charT> >
|
||||
std::basic_string<charT, traits, Allocator>
|
||||
generic_string(const Allocator& a = Allocator()) const;
|
||||
std::string generic_string() const {
|
||||
return _path;
|
||||
}
|
||||
std::wstring generic_wstring() const;
|
||||
std::string generic_u8string() const;
|
||||
std::u16string generic_u16string() const;
|
||||
std::u32string generic_u32string() const;
|
||||
|
||||
// compare
|
||||
int compare(const path& p) const noexcept;
|
||||
int compare(const std::string& s) const;
|
||||
int compare(const value_type* s) const;
|
||||
|
||||
// decomposition
|
||||
path root_name() const;
|
||||
path root_directory() const;
|
||||
path root_path() const;
|
||||
path relative_path() const;
|
||||
path parent_path() const;
|
||||
path filename() const;
|
||||
path stem() const;
|
||||
path extension() const;
|
||||
|
||||
// query
|
||||
bool empty() const noexcept {
|
||||
return _path.empty();
|
||||
}
|
||||
|
||||
bool has_root_name() const;
|
||||
bool has_root_directory() const;
|
||||
bool has_root_path() const;
|
||||
bool has_relative_path() const;
|
||||
bool has_parent_path() const;
|
||||
bool has_filename() const;
|
||||
bool has_stem() const;
|
||||
bool has_extension() const;
|
||||
|
||||
bool is_absolute() const {
|
||||
return !empty() && _path[0] == '/';
|
||||
}
|
||||
|
||||
bool is_relative() const {
|
||||
return empty() || _path[0] != '/';
|
||||
}
|
||||
|
||||
// iterators
|
||||
class iterator;
|
||||
typedef iterator const_iterator;
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
private:
|
||||
|
||||
void invalidate() const {
|
||||
_info.valid = false;
|
||||
}
|
||||
|
||||
void study() const;
|
||||
path &append_common(const std::string &s);
|
||||
|
||||
|
||||
|
||||
|
||||
string_type _path;
|
||||
|
||||
mutable struct {
|
||||
bool valid = false;
|
||||
value_type special;
|
||||
int stem;
|
||||
int extension;
|
||||
} _info;
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline void swap(path& lhs, path& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
/*
|
||||
// ugh, this is boost, not stl.
|
||||
inline std::size_t hash_value(const path& p) noexcept {
|
||||
return std::hash_value(p._path);
|
||||
}
|
||||
*/
|
||||
inline bool operator==(const path& lhs, const path& rhs) noexcept {
|
||||
return lhs.compare(rhs) == 0;
|
||||
}
|
||||
|
||||
inline bool operator!=(const path& lhs, const path& rhs) noexcept {
|
||||
return lhs.compare(rhs) != 0;
|
||||
}
|
||||
|
||||
inline bool operator< (const path& lhs, const path& rhs) noexcept {
|
||||
return lhs.compare(rhs) < 0;
|
||||
}
|
||||
inline bool operator<=(const path& lhs, const path& rhs) noexcept {
|
||||
return lhs.compare(rhs) <= 0;
|
||||
}
|
||||
inline bool operator> (const path& lhs, const path& rhs) noexcept {
|
||||
return lhs.compare(rhs) > 0;
|
||||
}
|
||||
inline bool operator>=(const path& lhs, const path& rhs) noexcept {
|
||||
return lhs.compare(rhs) >= 0;
|
||||
}
|
||||
inline path operator/ (const path& lhs, const path& rhs) {
|
||||
path tmp = lhs;
|
||||
return tmp.append(rhs);
|
||||
}
|
||||
|
||||
//template <class charT, class traits>
|
||||
//basic_ostream<charT, traits>&
|
||||
//operator<<(basic_ostream<charT, traits>& os, const path& p);
|
||||
//template <class charT, class traits>
|
||||
//basic_istream<charT, traits>&
|
||||
///operator>>(basic_istream<charT, traits>& is, path& p);
|
||||
template <class Source>
|
||||
path u8path(Source const& source);
|
||||
template <class InputIterator>
|
||||
path u8path(InputIterator begin, InputIterator end);
|
||||
|
||||
|
||||
class filesystem_error : public system_error
|
||||
{
|
||||
public:
|
||||
filesystem_error(const std::string& what_arg, error_code ec) :
|
||||
system_error(ec, what_arg)
|
||||
{}
|
||||
|
||||
filesystem_error(const std::string& what_arg, const path& p1, error_code ec) :
|
||||
system_error(ec, what_arg), _p1(p1)
|
||||
{}
|
||||
|
||||
filesystem_error(const std::string& what_arg, const path& p1, const path& p2, error_code ec) :
|
||||
system_error(ec, what_arg), _p1(p1), _p2(p2)
|
||||
{}
|
||||
|
||||
const path& path1() const noexcept {
|
||||
return _p1;
|
||||
}
|
||||
const path& path2() const noexcept {
|
||||
return _p2;
|
||||
}
|
||||
|
||||
//const char* what() const noexcept;
|
||||
|
||||
private:
|
||||
path _p1;
|
||||
path _p2;
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum class file_type {
|
||||
|
||||
none = 0,
|
||||
not_found = -1,
|
||||
regular = 1,
|
||||
directory = 2,
|
||||
symlink = 3,
|
||||
block = 4,
|
||||
character = 5,
|
||||
fifo = 6,
|
||||
socket = 7,
|
||||
unknown = 8
|
||||
};
|
||||
|
||||
enum perms {
|
||||
|
||||
none = 0,
|
||||
owner_read = 0400,
|
||||
owner_write = 0200,
|
||||
owner_exec = 0100,
|
||||
owner_all = 0700,
|
||||
|
||||
group_read = 040,
|
||||
group_write = 020,
|
||||
group_exec = 010,
|
||||
group_all = 070,
|
||||
|
||||
others_read = 04,
|
||||
others_write = 02,
|
||||
others_exec = 01,
|
||||
others_all = 07,
|
||||
|
||||
all = 0777,
|
||||
set_uid = 04000,
|
||||
set_gid = 02000,
|
||||
sticky_bit = 01000,
|
||||
mask = 07777,
|
||||
|
||||
unknown = 0xffff,
|
||||
|
||||
add_perms = 0x10000,
|
||||
remove_perms = 0x20000,
|
||||
resolve_symlinks = 0x40000,
|
||||
|
||||
};
|
||||
|
||||
|
||||
class file_status
|
||||
{
|
||||
public:
|
||||
// constructors
|
||||
explicit file_status(file_type ft = file_type::none, perms prms = perms::unknown) noexcept:
|
||||
_ft(ft), _prms(prms) {}
|
||||
|
||||
file_status(const file_status&) noexcept = default;
|
||||
file_status(file_status&&) noexcept = default;
|
||||
~file_status() = default;
|
||||
|
||||
file_status& operator=(const file_status&) noexcept = default;
|
||||
file_status& operator=(file_status&&) noexcept = default;
|
||||
|
||||
// observers
|
||||
file_type type() const noexcept {
|
||||
return _ft;
|
||||
}
|
||||
|
||||
perms permissions() const noexcept {
|
||||
return _prms;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
void type(file_type ft) noexcept {
|
||||
_ft = ft;
|
||||
}
|
||||
void permissions(perms prms) noexcept {
|
||||
_prms = prms;
|
||||
}
|
||||
|
||||
private:
|
||||
file_type _ft = file_type::none;
|
||||
perms _prms = perms::unknown;
|
||||
};
|
||||
|
||||
|
||||
struct space_info // returned by space function
|
||||
{
|
||||
uintmax_t capacity;
|
||||
uintmax_t free;
|
||||
uintmax_t available; // free space available to a non-privileged process
|
||||
};
|
||||
|
||||
enum class directory_options
|
||||
{
|
||||
none,
|
||||
follow_directory_symlink,
|
||||
skip_permission_denied
|
||||
};
|
||||
|
||||
//typedef std::chrono::time_point<trivial-clock> file_time_type;
|
||||
typedef std::chrono::time_point<std::chrono::system_clock> file_time_type;
|
||||
|
||||
enum class copy_options
|
||||
{
|
||||
none = 0,
|
||||
skip_existing = 1,
|
||||
overwrite_existing = 2,
|
||||
update_existing = 4,
|
||||
recurive = 8,
|
||||
copy_symlinks = 16,
|
||||
skip_symlinks = 32,
|
||||
directories_only = 64,
|
||||
create_symlinks = 128,
|
||||
create_hard_links = 256
|
||||
};
|
||||
|
||||
path current_path();
|
||||
path current_path(error_code& ec);
|
||||
void current_path(const path& p);
|
||||
void current_path(const path& p, error_code& ec) noexcept;
|
||||
|
||||
path absolute(const path& p, const path& base=current_path());
|
||||
|
||||
path canonical(const path& p, const path& base = current_path());
|
||||
path canonical(const path& p, error_code& ec);
|
||||
path canonical(const path& p, const path& base, error_code& ec);
|
||||
|
||||
void copy(const path& from, const path& to);
|
||||
void copy(const path& from, const path& to, error_code& ec) noexcept;
|
||||
void copy(const path& from, const path& to, copy_options options);
|
||||
void copy(const path& from, const path& to, copy_options options, error_code& ec) noexcept;
|
||||
|
||||
bool copy_file(const path& from, const path& to);
|
||||
bool copy_file(const path& from, const path& to, error_code& ec) noexcept;
|
||||
bool copy_file(const path& from, const path& to, copy_options option);
|
||||
bool copy_file(const path& from, const path& to, copy_options option, error_code& ec) noexcept;
|
||||
void copy_symlink(const path& existing_symlink, const path& new_symlink);
|
||||
|
||||
void copy_symlink(const path& existing_symlink, const path& new_symlink, error_code& ec) noexcept;
|
||||
|
||||
bool create_directories(const path& p);
|
||||
bool create_directories(const path& p, error_code& ec) noexcept;
|
||||
|
||||
bool create_directory(const path& p);
|
||||
bool create_directory(const path& p, error_code& ec) noexcept;
|
||||
bool create_directory(const path& p, const path& attributes);
|
||||
bool create_directory(const path& p, const path& attributes, error_code& ec) noexcept;
|
||||
|
||||
void create_directory_symlink(const path& to, const path& new_symlink);
|
||||
void create_directory_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept;
|
||||
|
||||
void create_hard_link(const path& to, const path& new_hard_link);
|
||||
void create_hard_link(const path& to, const path& new_hard_link, error_code& ec) noexcept;
|
||||
|
||||
void create_symlink(const path& to, const path& new_symlink);
|
||||
void create_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept;
|
||||
|
||||
|
||||
|
||||
|
||||
file_status status(const path& p);
|
||||
file_status status(const path& p, error_code& ec) noexcept;
|
||||
|
||||
file_status symlink_status(const path& p);
|
||||
file_status symlink_status(const path& p, error_code& ec) noexcept;
|
||||
|
||||
inline bool status_known(file_status s) noexcept {
|
||||
return s.type() != file_type::none;
|
||||
}
|
||||
|
||||
inline bool exists(file_status s) noexcept {
|
||||
return status_known(s) && s.type() != file_type::not_found;
|
||||
}
|
||||
|
||||
inline bool exists(const path& p) {
|
||||
return exists(status(p));
|
||||
}
|
||||
inline bool exists(const path& p, error_code& ec) noexcept {
|
||||
return exists(status(p, ec));
|
||||
}
|
||||
|
||||
|
||||
bool equivalent(const path& p1, const path& p2);
|
||||
bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept;
|
||||
|
||||
uintmax_t file_size(const path& p);
|
||||
uintmax_t file_size(const path& p, error_code& ec) noexcept;
|
||||
|
||||
uintmax_t hard_link_count(const path& p);
|
||||
uintmax_t hard_link_count(const path& p, error_code& ec) noexcept;
|
||||
|
||||
inline bool is_block_file(file_status s) noexcept {
|
||||
return s.type() == file_type::block;
|
||||
}
|
||||
inline bool is_block_file(const path& p) {
|
||||
return is_block_file(status(p));
|
||||
}
|
||||
inline bool is_block_file(const path& p, error_code& ec) noexcept {
|
||||
return is_block_file(status(p, ec));
|
||||
}
|
||||
|
||||
inline bool is_character_file(file_status s) noexcept {
|
||||
return s.type() == file_type::character;
|
||||
}
|
||||
inline bool is_character_file(const path& p) {
|
||||
return is_character_file(status(p));
|
||||
}
|
||||
inline bool is_character_file(const path& p, error_code& ec) noexcept {
|
||||
return is_character_file(status(p, ec));
|
||||
}
|
||||
|
||||
inline bool is_directory(file_status s) noexcept {
|
||||
return s.type() == file_type::directory;
|
||||
}
|
||||
inline bool is_directory(const path& p) {
|
||||
return is_directory(status(p));
|
||||
}
|
||||
inline bool is_directory(const path& p, error_code& ec) noexcept {
|
||||
return is_directory(status(p, ec));
|
||||
}
|
||||
|
||||
bool is_empty(const path& p);
|
||||
bool is_empty(const path& p, error_code& ec) noexcept;
|
||||
|
||||
|
||||
inline bool is_fifo(file_status s) noexcept {
|
||||
return s.type() == file_type::fifo;
|
||||
}
|
||||
inline bool is_fifo(const path& p) {
|
||||
return is_fifo(status(p));
|
||||
}
|
||||
inline bool is_fifo(const path& p, error_code& ec) noexcept {
|
||||
return is_fifo(status(p, ec));
|
||||
}
|
||||
|
||||
inline bool is_socket(file_status s) noexcept {
|
||||
return s.type() == file_type::socket;
|
||||
}
|
||||
inline bool is_socket(const path& p) {
|
||||
return is_socket(status(p));
|
||||
}
|
||||
inline bool is_socket(const path& p, error_code& ec) noexcept {
|
||||
return is_socket(status(p, ec));
|
||||
}
|
||||
|
||||
|
||||
inline bool is_symlink(file_status s) noexcept {
|
||||
return s.type() == file_type::symlink;
|
||||
}
|
||||
inline bool is_symlink(const path& p) {
|
||||
return is_symlink(status(p));
|
||||
}
|
||||
inline bool is_symlink(const path& p, error_code& ec) noexcept {
|
||||
return is_symlink(status(p, ec));
|
||||
}
|
||||
|
||||
|
||||
inline bool is_regular_file(file_status s) noexcept {
|
||||
return s.type() == file_type::regular;
|
||||
}
|
||||
|
||||
inline bool is_regular_file(const path& p) {
|
||||
return is_regular_file(status(p));
|
||||
}
|
||||
|
||||
inline bool is_regular_file(const path& p, error_code& ec) noexcept {
|
||||
return is_regular_file(status(p, ec));
|
||||
}
|
||||
|
||||
inline bool is_other(file_status s) noexcept {
|
||||
return exists(s) && !is_regular_file(s) && ! is_directory(s) && !is_symlink(s);
|
||||
}
|
||||
|
||||
inline bool is_other(const path& p) {
|
||||
return is_other(status(p));
|
||||
}
|
||||
|
||||
inline bool is_other(const path& p, error_code& ec) noexcept {
|
||||
return is_other(status(p, ec));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
file_time_type last_write_time(const path& p);
|
||||
file_time_type last_write_time(const path& p, error_code& ec) noexcept;
|
||||
void last_write_time(const path& p, file_time_type new_time);
|
||||
void last_write_time(const path& p, file_time_type new_time, error_code& ec) noexcept;
|
||||
|
||||
void permissions(const path& p, perms prms);
|
||||
void permissions(const path& p, perms prms, error_code& ec) noexcept;
|
||||
|
||||
path read_symlink(const path& p);
|
||||
path read_symlink(const path& p, error_code& ec);
|
||||
bool remove(const path& p);
|
||||
bool remove(const path& p, error_code& ec) noexcept;
|
||||
uintmax_t remove_all(const path& p);
|
||||
uintmax_t remove_all(const path& p, error_code& ec) noexcept;
|
||||
void rename(const path& from, const path& to);
|
||||
void rename(const path& from, const path& to, error_code& ec) noexcept;
|
||||
void resize_file(const path& p, uintmax_t size);
|
||||
void resize_file(const path& p, uintmax_t size, error_code& ec) noexcept;
|
||||
space_info space(const path& p);
|
||||
space_info space(const path& p, error_code& ec) noexcept;
|
||||
file_status status(const path& p);
|
||||
file_status status(const path& p, error_code& ec) noexcept;
|
||||
bool status_known(file_status s) noexcept;
|
||||
file_status symlink_status(const path& p);
|
||||
file_status symlink_status(const path& p, error_code& ec) noexcept;
|
||||
path system_complete(const path& p);
|
||||
path system_complete(const path& p, error_code& ec);
|
||||
path temp_directory_path();
|
||||
path temp_directory_path(error_code& ec);
|
||||
|
||||
|
||||
|
||||
|
||||
class directory_entry {
|
||||
|
||||
public:
|
||||
|
||||
// constructors and destructor
|
||||
directory_entry() = default;
|
||||
directory_entry(const directory_entry&) = default;
|
||||
directory_entry(directory_entry&&) noexcept = default;
|
||||
explicit directory_entry(const path& p, file_status st=file_status(), file_status symlink_st=file_status());
|
||||
|
||||
~directory_entry() = default;
|
||||
|
||||
// modifiers
|
||||
directory_entry& operator=(const directory_entry&) = default;
|
||||
directory_entry& operator=(directory_entry&&) noexcept = default;
|
||||
void assign(const path& p, file_status st=file_status(),
|
||||
file_status symlink_st=file_status());
|
||||
void replace_filename(const path& p, file_status st=file_status(),
|
||||
file_status symlink_st=file_status());
|
||||
|
||||
|
||||
// observers
|
||||
const filesystem::path& path() const noexcept {
|
||||
return _path;
|
||||
}
|
||||
|
||||
file_status status() const;
|
||||
file_status status(error_code& ec) const noexcept;
|
||||
file_status symlink_status() const;
|
||||
file_status symlink_status(error_code& ec) const noexcept;
|
||||
|
||||
|
||||
bool operator< (const directory_entry& rhs) const noexcept;
|
||||
bool operator==(const directory_entry& rhs) const noexcept;
|
||||
bool operator!=(const directory_entry& rhs) const noexcept;
|
||||
bool operator<=(const directory_entry& rhs) const noexcept;
|
||||
bool operator> (const directory_entry& rhs) const noexcept;
|
||||
bool operator>=(const directory_entry& rhs) const noexcept;
|
||||
|
||||
|
||||
private:
|
||||
class path _path;
|
||||
mutable file_status _st;
|
||||
mutable file_status _lst;
|
||||
};
|
||||
|
||||
|
||||
class directory_iterator : public std::iterator<std::input_iterator_tag, directory_entry>
|
||||
{
|
||||
public:
|
||||
// member functions
|
||||
directory_iterator() noexcept = default;
|
||||
explicit directory_iterator(const path& p);
|
||||
directory_iterator(const path& p, error_code& ec) noexcept;
|
||||
|
||||
directory_iterator(const directory_iterator&) = default;
|
||||
|
||||
directory_iterator(directory_iterator&&) = default;
|
||||
~directory_iterator() = default;
|
||||
|
||||
directory_iterator& operator=(const directory_iterator&) = default;
|
||||
directory_iterator& operator=(directory_iterator&&) = default;
|
||||
|
||||
const directory_entry& operator*() const {
|
||||
return _imp->_entry;
|
||||
}
|
||||
const directory_entry* operator->() const {
|
||||
return &_imp->_entry;
|
||||
}
|
||||
directory_iterator& operator++();
|
||||
directory_iterator& increment(error_code& ec) noexcept;
|
||||
|
||||
bool operator == (const directory_iterator &rhs) const noexcept {
|
||||
return _imp == rhs._imp;
|
||||
}
|
||||
bool operator != (const directory_iterator &rhs) const noexcept {
|
||||
return _imp != rhs._imp;
|
||||
}
|
||||
|
||||
// other members as required by
|
||||
// C++ Std, 24.1.1 Input iterators [input.iterators]
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void open_dir(const path &p, error_code &ec) noexcept;
|
||||
|
||||
struct imp {
|
||||
path _path;
|
||||
directory_entry _entry;
|
||||
DIR *_dp = nullptr;
|
||||
|
||||
imp() = default;
|
||||
imp(const path &p, DIR *dp) : _path(p), _dp(dp)
|
||||
{}
|
||||
|
||||
~imp() {
|
||||
if (_dp) closedir(_dp);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<imp> _imp;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
directory_iterator non-member functions
|
||||
*/
|
||||
inline const directory_iterator& begin(const directory_iterator& iter) noexcept {
|
||||
return iter;
|
||||
}
|
||||
|
||||
inline directory_iterator end(const directory_iterator&) noexcept {
|
||||
return directory_iterator();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
inline void swap(filesystem::path& lhs, filesystem::path& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
template<>
|
||||
struct hash<filesystem::path>
|
||||
{
|
||||
std::size_t operator()(const filesystem::path &p) const
|
||||
{
|
||||
std::hash<filesystem::path::string_type> hasher;
|
||||
return hasher(p.native());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
285
cxx/path.cpp
Normal file
285
cxx/path.cpp
Normal file
@ -0,0 +1,285 @@
|
||||
|
||||
#include "filesystem.h"
|
||||
|
||||
|
||||
namespace filesystem {
|
||||
|
||||
namespace {
|
||||
const path::value_type separator = '/';
|
||||
|
||||
// hmmm... these could be pre-studied since they're constant?
|
||||
const path path_dot = ".";
|
||||
const path path_dotdot = "..";
|
||||
const path path_sep = "/";
|
||||
}
|
||||
|
||||
void path::study() const {
|
||||
|
||||
if (_info.valid) return;
|
||||
int length = _path.length();
|
||||
if (length == 0)
|
||||
{
|
||||
_info.valid = true;
|
||||
_info.special = 0;
|
||||
_info.stem = _info.extension = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
auto back = _path[length - 1];
|
||||
|
||||
// check for special cases (part 1)
|
||||
// if the path is all /s, filename is /
|
||||
// if the path ends with a / (but contains a non-/ char),
|
||||
// the filename is .
|
||||
|
||||
if (back == separator) {
|
||||
value_type special = '.';
|
||||
if (_path.find_first_not_of(separator) == _path.npos)
|
||||
special = separator;
|
||||
|
||||
_info.valid = true;
|
||||
_info.extension = length;
|
||||
_info.stem = length;
|
||||
_info.special = special;
|
||||
return;
|
||||
}
|
||||
|
||||
int stem = 0;
|
||||
int extension = length;
|
||||
for (int i = length; i; ) {
|
||||
auto c = _path[--i];
|
||||
if (c == '.' && extension == length)
|
||||
extension = i;
|
||||
if (c == '/') {
|
||||
stem = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check for special cases (part 2)
|
||||
// ".." and "." are not extensions.
|
||||
if (back == '.') {
|
||||
int xlength = length - stem;
|
||||
if (xlength == 1)
|
||||
extension = length;
|
||||
if (xlength == 2 && _path[stem] == '.')
|
||||
extension = length;
|
||||
}
|
||||
|
||||
_info.valid = true;
|
||||
_info.stem = stem;
|
||||
_info.extension = extension;
|
||||
_info.special = 0;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
path::path(const path &rhs) :
|
||||
_path(rhs._path), _info(rhs._info)
|
||||
{}
|
||||
|
||||
path::path(path &&rhs) noexcept :
|
||||
_path(std::move(rhs._path))
|
||||
{
|
||||
rhs.invalidate();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// private. neither this->_path nor s are empty.
|
||||
path &path::append_common(const std::string &s)
|
||||
{
|
||||
invalidate();
|
||||
if (_path.back() != separator && s.front() != separator)
|
||||
_path.push_back(separator);
|
||||
|
||||
_path.append(s);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
path& path::append(const path& p)
|
||||
{
|
||||
if (p.empty()) return *this;
|
||||
|
||||
if (empty()) {
|
||||
return (*this = p);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
|
||||
// check for something stupid like xx.append(xx);
|
||||
if (&p == this) {
|
||||
return append_common(string_type(p._path));
|
||||
}
|
||||
|
||||
return append_common(p._path);
|
||||
}
|
||||
|
||||
path& path::append(const string_type &s)
|
||||
{
|
||||
if (s.empty()) return *this;
|
||||
invalidate();
|
||||
|
||||
if (empty()) {
|
||||
_path = s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
if (&s == &_path) {
|
||||
string_type tmp(s);
|
||||
if (_path.back() != separator && tmp[0] != separator)
|
||||
_path.push_back(separator);
|
||||
_path.append(tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (_path.back() != separator && s[0] != separator)
|
||||
_path.push_back(separator);
|
||||
|
||||
_path.append(s);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
path path::filename() const {
|
||||
|
||||
if (empty()) return *this;
|
||||
if (!_info.valid) study();
|
||||
|
||||
if (_info.special == separator) return path_sep;
|
||||
if (_info.special == '.') return path_dot;
|
||||
|
||||
if (_info.stem == 0) return *this;
|
||||
return _path.substr(_info.stem);
|
||||
}
|
||||
|
||||
path path::stem() const {
|
||||
|
||||
// filename without the extension.
|
||||
|
||||
if (empty()) return *this;
|
||||
if (!_info.valid) study();
|
||||
|
||||
if (_info.special == separator) return path_sep;
|
||||
if (_info.special == '.') return path_dot;
|
||||
|
||||
return _path.substr(_info.stem, _info.extension - _info.stem);
|
||||
}
|
||||
|
||||
path path::extension() const {
|
||||
|
||||
if (empty()) return *this;
|
||||
if (!_info.valid) study();
|
||||
|
||||
return _path.substr(_info.extension);
|
||||
}
|
||||
|
||||
|
||||
bool path::has_parent_path() const
|
||||
{
|
||||
// if there is a /, it has a parent path.
|
||||
// ... unless it's /.
|
||||
|
||||
if (empty()) return false;
|
||||
|
||||
if (!_info.valid) study();
|
||||
|
||||
if (_info.special == '/') return false;
|
||||
|
||||
return _path.find(separator) != _path.npos;
|
||||
}
|
||||
|
||||
path path::parent_path() const {
|
||||
|
||||
/*
|
||||
* special cases:
|
||||
* /abc -> /
|
||||
* /abc/ -> /abc
|
||||
* all trailing /s are removed.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
if (empty()) return *this;
|
||||
|
||||
if (!_info.valid) study();
|
||||
|
||||
// "/" is a file of "/" with a parent of ""
|
||||
if (_info.special == separator) return path();
|
||||
|
||||
// stem starts at 0, eg "abc"
|
||||
if (!_info.stem) return path();
|
||||
|
||||
|
||||
auto tmp = _path.substr(0, _info.stem - 1);
|
||||
|
||||
// remove trailing slashes, but return "/" if nothing BUT /s.
|
||||
while (!tmp.empty() && tmp.back() == separator) tmp.pop_back();
|
||||
if (tmp.empty()) return path_sep;
|
||||
|
||||
return path(tmp);
|
||||
}
|
||||
|
||||
path path::root_directory() const {
|
||||
// for unix, root directory is / or "".
|
||||
if (empty()) return *this;
|
||||
|
||||
return _path.front() == '/' ? path_sep : path();
|
||||
}
|
||||
|
||||
path path::root_name() const {
|
||||
/*
|
||||
* boost (unix) considers // or //component
|
||||
* to be a root name (and only those cases).
|
||||
*
|
||||
* I do not.
|
||||
*/
|
||||
|
||||
return path();
|
||||
}
|
||||
|
||||
path path::root_path() const {
|
||||
// root_name + root_directory.
|
||||
// since root_name is always empty...
|
||||
|
||||
return root_directory();
|
||||
}
|
||||
|
||||
|
||||
path path::relative_path() const {
|
||||
// first pathname *after* the root path
|
||||
// root_path is first / in this implementation.
|
||||
|
||||
if (is_relative()) return *this;
|
||||
|
||||
auto pos = _path.find_first_not_of(separator);
|
||||
if (pos == _path.npos) return path();
|
||||
|
||||
return path(_path.substr(pos));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// compare
|
||||
int path::compare(const path& p) const noexcept {
|
||||
if (&p == this) return 0;
|
||||
return _path.compare(p._path);
|
||||
}
|
||||
|
||||
int path::compare(const std::string& s) const {
|
||||
if (&s == &_path) return 0;
|
||||
return _path.compare(s);
|
||||
}
|
||||
|
||||
int path::compare(const value_type* s) const {
|
||||
return _path.compare(s);
|
||||
}
|
||||
|
||||
|
||||
}
|
81
cxx/string_splitter.h
Normal file
81
cxx/string_splitter.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef __string_splitter__
|
||||
#define __string_splitter__
|
||||
|
||||
#include <string>
|
||||
|
||||
class string_splitter {
|
||||
public:
|
||||
string_splitter(const std::string &str, char sep) :
|
||||
_parent(str), _sep(sep)
|
||||
{
|
||||
_begin = 0;
|
||||
_end = _parent.find(_sep);
|
||||
_str = _parent.substr(_begin, _end);
|
||||
// _begin is 0, _end is either npos or offset from 0,
|
||||
// so no need to calculate a count.
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return _begin != npos;
|
||||
}
|
||||
|
||||
string_splitter &operator++() {
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::string &operator *() const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
const std::string *operator ->() const {
|
||||
return &_str;
|
||||
}
|
||||
|
||||
private:
|
||||
void increment() {
|
||||
_str.clear();
|
||||
if (_begin == npos) return;
|
||||
if (_end == npos) { _begin = _end; return; }
|
||||
|
||||
_begin = _end + 1;
|
||||
_end = _parent.find(_sep, _begin);
|
||||
auto count = _end == npos ? _end : _end - _begin;
|
||||
_str = _parent.substr(_begin, count);
|
||||
}
|
||||
|
||||
const static auto npos = std::string::npos;
|
||||
std::string _str;
|
||||
const std::string &_parent;
|
||||
char _sep;
|
||||
std::string::size_type _begin = 0;
|
||||
std::string::size_type _end = 0;
|
||||
};
|
||||
|
||||
|
||||
#ifdef TEST
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: %s sep string\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (strlen(argv[1]) != 1) {
|
||||
fprintf(stderr, "Separator must be a single character\n");
|
||||
return 1;
|
||||
}
|
||||
char sep = argv[1][0];
|
||||
std::string str(argv[2]);
|
||||
|
||||
for (auto iter = string_splitter(str, sep); iter; ++iter) {
|
||||
printf("%s\n", iter->c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -17,7 +17,7 @@
|
||||
#include "phase2.h"
|
||||
#include "command.h"
|
||||
|
||||
#include "mapped_file.h"
|
||||
#include "cxx/mapped_file.h"
|
||||
#include "error.h"
|
||||
|
||||
//#include <histedit.h>
|
||||
|
Loading…
Reference in New Issue
Block a user