From 5ffb56b494923a8efeb818f432210f88f63b21e0 Mon Sep 17 00:00:00 2001 From: John Soklaski Date: Tue, 1 Aug 2017 23:42:07 -0600 Subject: [PATCH 1/3] Support Unicode reading/writing unicode paths on Windows --- setup.py | 2 +- src/ccc.cpp | 35 ++++++++++++++++++++++++++++++++--- src/ccc.h | 2 +- src/compiler.cpp | 9 +++++---- src/module.cpp | 4 ++-- src/pythonlib.cpp | 4 ++-- src/util.h | 16 ++++++++++++++++ 7 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 src/util.h diff --git a/setup.py b/setup.py index 973b826..593dc41 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ extra_link_args = ["-lstdc++fs"] setup(name="ccscript", - version="1.338", + version="1.339", description="ccscript", url="http://starmen.net/pkhack/ccscript", ext_modules=[ diff --git a/src/ccc.cpp b/src/ccc.cpp index 322e4ed..478744f 100644 --- a/src/ccc.cpp +++ b/src/ccc.cpp @@ -5,13 +5,17 @@ #include #include #include +#include +#include #include #include namespace fs = std::experimental::filesystem::v1; +#include "ccc.h" #include "compiler.h" #include "module.h" +#include "util.h" using std::vector; using std::string; @@ -32,7 +36,7 @@ string getbasepath(const char* p) void printversion() { - cout << "ccc version 1.337 Duck Tape Edition" << endl; + cout << "ccc version 1.339 Duck Tape Edition" << endl; } void printusage() @@ -64,9 +68,34 @@ void printusage() << " put the resulting compiled text at $F20000 in the ROM Earthbound.smc" << endl; } -int main(int argc, char* argv[]) +#ifdef _WIN32 +int wmain(int argc, wchar_t* argv[]) { + std::wstring_convert, wchar_t> converter; + // Convert the utf-16 args to utf-8 + std::vector utf8Args; + for(int i = 0; i < argc; ++i) { + auto utf8Arg = converter.to_bytes(argv[i]); + utf8Args.push_back(utf8Arg); + } + + // Expose the std::strings as a vector of const char*s + std::vector utf8Argv; + std::transform(utf8Args.begin(), utf8Args.end(), std::back_inserter(utf8Argv), + [](const std::string& s){ return s.c_str(); } ); + + return run(argc, utf8Argv.data()); +} +#else +int main(int argc, const char* argv[]) +{ + return run(argc, argv); +} +#endif + +int run(int argc, const char* argv[]) +{ // // Get the default libs path // @@ -269,7 +298,7 @@ int main(int argc, char* argv[]) if(!summaryfile.empty()) { std::fstream file; - file.open(summaryfile.c_str(), std::ios_base::out|std::ios_base::trunc); + file.open(ConvertToNativeString(summaryfile), std::ios_base::out|std::ios_base::trunc); if(file.fail()) { std::cerr << "Couldn't open " << summaryfile << " to write summary file." << std::endl; diff --git a/src/ccc.h b/src/ccc.h index 3556e31..7e513e7 100644 --- a/src/ccc.h +++ b/src/ccc.h @@ -1 +1 @@ -int main(int argc, char* argv[]); +int run(int argc, const char* argv[]); \ No newline at end of file diff --git a/src/compiler.cpp b/src/compiler.cpp index 1125c08..df1121d 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -20,6 +20,7 @@ namespace fs = std::experimental::filesystem::v1; #include "module.h" #include "symboltable.h" #include "exception.h" +#include "util.h" using namespace std; @@ -60,7 +61,7 @@ Compiler::Compiler(const string& romfile, unsigned int adr, unsigned int endadr) nostdlibs = false; // Open the file - ifstream file(filename.c_str(), ifstream::binary); + ifstream file(ConvertToNativeString(filename), ifstream::binary); if(file.fail()) { Error("failed to open file " + filename + " for reading."); @@ -130,7 +131,7 @@ Compiler::~Compiler() void Compiler::WriteOutput() { if(failed) return; - ofstream file(filename.c_str(), ofstream::binary); + ofstream file(ConvertToNativeString(filename), ofstream::binary); if(file.fail()) { Error("failed to open file " + filename + " for writing."); @@ -576,7 +577,7 @@ void Compiler::DoDelayedWrites() */ void Compiler::WriteResetInfo(const std::string &filename) { - ofstream file(filename.c_str()); + ofstream file(ConvertToNativeString(filename)); if(file.fail()) throw Exception("couldn't create info file '" + filename + "'"); @@ -621,7 +622,7 @@ void Compiler::WriteResetInfo(const std::string &filename) void Compiler::ApplyResetInfo(const std::string& filename) { - ifstream file(filename.c_str()); + ifstream file(ConvertToNativeString(filename)); if(file.fail()) return; diff --git a/src/module.cpp b/src/module.cpp index 2f62866..eba305b 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -16,6 +16,7 @@ #include "symboltable.h" #include "bytechunk.h" #include "exception.h" +#include "util.h" using namespace std; @@ -68,9 +69,8 @@ void Module::Load(const string& filename) failed = true; return; } - ifstream in(filename.c_str()); - + ifstream in(ConvertToNativeString(filename)); if(in.fail()) { parent->Error("couldn't open " + filename); diff --git a/src/pythonlib.cpp b/src/pythonlib.cpp index 57fd936..1deaf20 100644 --- a/src/pythonlib.cpp +++ b/src/pythonlib.cpp @@ -19,7 +19,7 @@ static PyObject* ccc(PyObject* self, PyObject* args) { return nullptr; } - std::vector argv; + std::vector argv; argv.reserve(argc+1); argv.push_back(CCC_BASENAME); for (int i = 0; i < argc; ++i) { @@ -39,7 +39,7 @@ static PyObject* ccc(PyObject* self, PyObject* args) { std::stringstream buffer; std::streambuf* old_cout = std::cout.rdbuf(buffer.rdbuf()); std::streambuf* old_cerr = std::cerr.rdbuf(buffer.rdbuf()); - int return_value = main(argc + 1, &argv[0]); + int return_value = run(argc + 1, argv.data()); std::cout.rdbuf(old_cout); std::cerr.rdbuf(old_cerr); diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..924051d --- /dev/null +++ b/src/util.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef _WIN32 +inline std::wstring ConvertToNativeString(const std::string str) { + std::wstring_convert> converter; + std::wstring wstr = converter.from_bytes(str); + return wstr; +} +#else +inline std::string ConvertToNativeString(const std::string str) { + return str; +} +#endif \ No newline at end of file From 5117fcf664f1bd5666ed5f65f9e311aae6e0cd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silva?= Date: Fri, 12 Jul 2019 23:18:06 +0100 Subject: [PATCH 2/3] Slight improvements and optimizations. --- setup.py | 4 +-- src/bytechunk.cpp | 12 +++---- src/ccc.cpp | 81 ++++++++++++++++++++--------------------------- src/ccc.h | 3 +- src/compiler.cpp | 6 ++-- src/compiler.h | 2 +- src/pythonlib.cpp | 2 +- 7 files changed, 50 insertions(+), 60 deletions(-) mode change 100644 => 100755 setup.py diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 593dc41..fbd3a61 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env/python +#!/usr/bin/python3 import os import platform @@ -26,4 +26,4 @@ extra_compile_args=extra_compile_args, extra_link_args=extra_link_args ) - ]) \ No newline at end of file + ]) diff --git a/src/bytechunk.cpp b/src/bytechunk.cpp index b1367ff..25e3932 100644 --- a/src/bytechunk.cpp +++ b/src/bytechunk.cpp @@ -166,7 +166,7 @@ void ByteChunk::Truncate(unsigned int newsize) bytes.resize(newsize); pos = bytes.size(); - + cinfo.resize(newsize); } @@ -253,7 +253,7 @@ void ByteChunk::TranslateReferences(ByteChunk& destination, } } - + for(vector::const_iterator it = needed_refs.begin(); it != needed_refs.end(); ++it) { @@ -307,7 +307,7 @@ void ByteChunk::TranslateReferences(ByteChunk& destination, // how many total bytes of the reference are left? r.length = std::min(r.length, r.length - r.offset - overflow); } - + // Add the reference destination.AddReference(r.location + start - offset, r.offset, r.length, r.target); } @@ -442,7 +442,7 @@ unsigned char ByteChunk::ReadByte(unsigned int pos) const try { return bytes.at(pos); } - catch(std::exception e) {} + catch(std::exception&) {} return 0; } @@ -453,7 +453,7 @@ unsigned short ByteChunk::ReadShort(unsigned int pos) const result += bytes.at(pos); result += bytes.at(pos+1) << 8; } - catch(std::exception e) { + catch(std::exception&) { // just give incomplete results on exception } return result; @@ -468,7 +468,7 @@ unsigned int ByteChunk::ReadLong(unsigned int pos) const result += bytes.at(pos+2) << 16; result += bytes.at(pos+3) << 24; } - catch(std::exception e) { + catch(std::exception&) { } return result; } diff --git a/src/ccc.cpp b/src/ccc.cpp index 478744f..c4b9431 100644 --- a/src/ccc.cpp +++ b/src/ccc.cpp @@ -5,13 +5,16 @@ #include #include #include -#include -#include #include #include namespace fs = std::experimental::filesystem::v1; +#ifdef _WIN32 +#include +#include +#endif + #include "ccc.h" #include "compiler.h" #include "module.h" @@ -23,8 +26,6 @@ using std::stringstream; using std::cout; using std::endl; - - string getbasepath(const char* p) { string path = p; @@ -52,8 +53,6 @@ void printusage() << " --nostdlibs Do not include the default standard libraries" << endl << " --summary Writes a compilation summary to " << endl << " Useful if you want to know where stuff went." << endl - //<< " --shortpause Short pauses '/' are frames long (default 5)" << endl - //<< " --longpause Long pauses '|' are frames long (default 15)" << endl << " --printAST Prints the abstract syntax tree for each module" << endl << " --printRT Prints the root symbol table for each module" << endl << " --printJumps Prints the compiled addresses of all labels" << endl @@ -68,33 +67,7 @@ void printusage() << " put the resulting compiled text at $F20000 in the ROM Earthbound.smc" << endl; } -#ifdef _WIN32 -int wmain(int argc, wchar_t* argv[]) -{ - std::wstring_convert, wchar_t> converter; - - // Convert the utf-16 args to utf-8 - std::vector utf8Args; - for(int i = 0; i < argc; ++i) { - auto utf8Arg = converter.to_bytes(argv[i]); - utf8Args.push_back(utf8Arg); - } - - // Expose the std::strings as a vector of const char*s - std::vector utf8Argv; - std::transform(utf8Args.begin(), utf8Args.end(), std::back_inserter(utf8Argv), - [](const std::string& s){ return s.c_str(); } ); - - return run(argc, utf8Argv.data()); -} -#else -int main(int argc, const char* argv[]) -{ - return run(argc, argv); -} -#endif - -int run(int argc, const char* argv[]) +int cccmain(int argc, const char* argv[]) { // // Get the default libs path @@ -118,8 +91,6 @@ int run(int argc, const char* argv[]) vector libs; bool noreset = false; bool nostdlibs = false; - unsigned char shortpause; - unsigned char longpause; bool printAST = false; bool printRT = false; bool printJumps = false; @@ -134,8 +105,6 @@ int run(int argc, const char* argv[]) // --libs look in for standard libraries // -h,--help print help message // --nostdlibs do not include default libraries - // --shortpause duration of short pauses ('/') - // --longpause duration of long pauses ('|') // --printAST print AST for each module // --printRT print root table for each module // --printJumps print a list of jumps and addresses @@ -212,14 +181,6 @@ int run(int argc, const char* argv[]) } summaryfile = argv[p++]; } - else if(!strcmp(argv[p],"--shortpause")) { - p++; - shortpause = (unsigned char)strtoul(argv[p++], NULL, 10); - } - else if(!strcmp(argv[p],"--longpause")) { - p++; - longpause = (unsigned char)strtoul(argv[p++], NULL, 10); - } else if(!strcmp(argv[p],"--printAST")) { p++; printAST = true; @@ -308,4 +269,32 @@ int run(int argc, const char* argv[]) } return compiler.Failed(); -} \ No newline at end of file +} + +#ifdef _WIN32 +int wmain(int argc, const wchar_t* argv[]) +{ + std::vector utf8Args; + std::vector utf8Argv; + utf8Args.reserve(argc); + utf8Args.reserve(argc); + + { + std::wstring_convert, wchar_t> converter; + + // Convert the utf-16 args to utf-8... + // and expose the std::strings as a vector of const char*s + for(int i = 0; i < argc; ++i) { + utf8Args.emplace_back(converter.to_bytes(argv[i])); + utf8Argv.emplace_back(utf8Args[i].c_str()); + } + } + + return cccmain(argc, utf8Argv.data()); +} +#else +int main(int argc, const char* argv[]) +{ + return cccmain(argc, argv); +} +#endif diff --git a/src/ccc.h b/src/ccc.h index 7e513e7..a96d848 100644 --- a/src/ccc.h +++ b/src/ccc.h @@ -1 +1,2 @@ -int run(int argc, const char* argv[]); \ No newline at end of file +extern "C" int cccmain(int argc, const char* argv[]); +extern "C" int main(int argc, const char* argv[]); diff --git a/src/compiler.cpp b/src/compiler.cpp index df1121d..ad4c786 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -85,7 +85,7 @@ Compiler::Compiler(const string& romfile, unsigned int adr, unsigned int endadr) file.seekg(0, ifstream::end); filesize = file.tellg(); file.seekg(0); - filebuffer = new char[filesize]; + filebuffer = new char[(unsigned int) filesize]; file.read(filebuffer, filesize); file.close(); @@ -472,7 +472,7 @@ void Compiler::OutputModules() throw Exception(ss.str()); } - m->WriteCode(filebuffer, MapVirtualAddress(m->GetBaseAddress()), filesize); + m->WriteCode(filebuffer, MapVirtualAddress(m->GetBaseAddress()), (int) filesize); if(printJumps && m->GetName().substr(0,3) != "std") m->PrintJumps(); @@ -555,7 +555,7 @@ void Compiler::DoDelayedWrites() << romwrites[i]->GetVirtualAddress(); throw Exception(ss.str()); } - romwrites[i]->DoWrite(filebuffer, padr, filesize); + romwrites[i]->DoWrite(filebuffer, padr, (int) filesize); } } diff --git a/src/compiler.h b/src/compiler.h index 6382bd0..8a3796f 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -80,7 +80,7 @@ class Compiler // File info std::string filename; char* filebuffer; - int filesize; + std::streamoff filesize; int actual_start; int actual_end; int totalfrag; diff --git a/src/pythonlib.cpp b/src/pythonlib.cpp index 1deaf20..c96a7b6 100644 --- a/src/pythonlib.cpp +++ b/src/pythonlib.cpp @@ -39,7 +39,7 @@ static PyObject* ccc(PyObject* self, PyObject* args) { std::stringstream buffer; std::streambuf* old_cout = std::cout.rdbuf(buffer.rdbuf()); std::streambuf* old_cerr = std::cerr.rdbuf(buffer.rdbuf()); - int return_value = run(argc + 1, argv.data()); + int return_value = cccmain(argc + 1, argv.data()); std::cout.rdbuf(old_cout); std::cerr.rdbuf(old_cerr); From cc302ee9e45a9a7896158afa365173e775635113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Silva?= Date: Tue, 30 Jul 2019 00:54:17 +0100 Subject: [PATCH 3/3] Use a small implementation of std::filesystem on POSIX systems and lower the required C++ version to C++11. --- setup.py | 28 ++-- src/Makefile | 9 +- src/ccc.cpp | 14 +- src/compiler.cpp | 17 ++- src/filesystem/LICENSE | 36 ++++++ src/filesystem/filesystem_mini.h | 211 +++++++++++++++++++++++++++++++ src/tests/src/main.cpp | 8 +- src/util.h | 3 +- 8 files changed, 289 insertions(+), 37 deletions(-) create mode 100644 src/filesystem/LICENSE create mode 100644 src/filesystem/filesystem_mini.h diff --git a/setup.py b/setup.py index fbd3a61..3a65f2b 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,6 @@ import os import platform - from setuptools import setup from setuptools.extension import Extension @@ -11,19 +10,18 @@ extra_compile_args = [] extra_link_args = [] -if platform.system() == "Linux": - extra_compile_args = ["-std=c++14"] - extra_link_args = ["-lstdc++fs"] +if platform.system() == "Linux" or platform.system() == "Darwin": + extra_compile_args = ["-std=c++11"] setup(name="ccscript", - version="1.339", - description="ccscript", - url="http://starmen.net/pkhack/ccscript", - ext_modules=[ - Extension("ccscript", - source_files, - language="c++", - extra_compile_args=extra_compile_args, - extra_link_args=extra_link_args - ) - ]) + version="1.339", + description="ccscript", + url="http://starmen.net/pkhack/ccscript", + ext_modules=[ + Extension("ccscript", + source_files, + language="c++", + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args + ) + ]) diff --git a/src/Makefile b/src/Makefile index 7a94ecb..cd2ca17 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,13 +1,12 @@ # # Crappy makefile for ccscript compiler (ccc) # -CXXFLAGS = -c -Wall -O3 -std=c++14 +CXXFLAGS = -c -Wall -O3 -std=c++11 OBJDIR = obj BINDIR = bin OUTFILE = ccc SOURCES = ccc.cpp compiler.cpp module.cpp bytechunk.cpp lexer.cpp parser.cpp ast.cpp \ stringparser.cpp symboltable.cpp table.cpp value.cpp anchor.cpp -LIBS = -lstdc++fs OBJECTS = $(SOURCES:%.cpp=$(OBJDIR)/%.o) INSTALL_DIR = /usr/local @@ -39,13 +38,13 @@ endif # # Targets # - + all: ccc tests runtests # Builds the compiler ccc: mkdirs libsdir $(OBJECTS) - $(CXX) $(OBJECTS) $(LIBS) -o $(BINDIR)/$@ + $(CXX) $(OBJECTS) -o $(BINDIR)/$@ # Ensure that bin and obj directories exist @@ -111,5 +110,5 @@ $(OBJDIR)/table.o: table.h clean: -$(RM) $(OBJDIR)$(SEP)*.o $(BINDIR)$(SEP)$(OUTFILE) -$(MAKE) -C tests clean - + diff --git a/src/ccc.cpp b/src/ccc.cpp index c4b9431..37ab511 100644 --- a/src/ccc.cpp +++ b/src/ccc.cpp @@ -7,12 +7,14 @@ #include #include -#include -namespace fs = std::experimental::filesystem::v1; - #ifdef _WIN32 -#include -#include + #include + #include + #include + namespace fs = std::experimental::filesystem::v1; +#else + #include "filesystem/filesystem_mini.h" + namespace fs = filesystem; #endif #include "ccc.h" @@ -259,7 +261,7 @@ int cccmain(int argc, const char* argv[]) if(!summaryfile.empty()) { std::fstream file; - file.open(ConvertToNativeString(summaryfile), std::ios_base::out|std::ios_base::trunc); + file.open(ConvertToNativeString(summaryfile).c_str(), std::ios_base::out|std::ios_base::trunc); if(file.fail()) { std::cerr << "Couldn't open " << summaryfile << " to write summary file." << std::endl; diff --git a/src/compiler.cpp b/src/compiler.cpp index ad4c786..b0cdd41 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -9,8 +9,13 @@ #include #include -#include -namespace fs = std::experimental::filesystem::v1; +#ifdef _WIN32 + #include + namespace fs = std::experimental::filesystem::v1; +#else + #include "filesystem/filesystem_mini.h" + namespace fs = filesystem; +#endif #include "module.h" @@ -166,7 +171,7 @@ Module* Compiler::LoadModule(const std::string &filename) // instead of SetLibTable, we should use m->Include(othermodule). //m->SetLibTable(libtable); string name = m->GetName(); - + if(GetModule(name) != NULL) { Error("attempt to redefine module " + name + "; module names must be unique"); return NULL; @@ -193,7 +198,7 @@ string Compiler::FindModule(const string& name, const string& filedir) { // Complete paths aren't looked for in include directories if (fs::path(name).is_absolute()) - return fs::exists( name )? name : ""; + return fs::exists( name ) ? name : ""; // First, try in the provided file directory. fs::path base(filedir); @@ -415,8 +420,8 @@ void Compiler::AssignModuleAddresses() bool found = false; for(i = sorted.begin(); i != sorted.end(); ++i) { - unsigned int size = (*i)->GetCodeSize(); - if((base & 0xFFFF) + size <= 0x10000) + unsigned int size = (*i)->GetCodeSize(); + if((base & 0xFFFF) + size <= 0x10000) { // Check for overwriting maximum address if((endadr > 0) && (base + size >= endadr)) diff --git a/src/filesystem/LICENSE b/src/filesystem/LICENSE new file mode 100644 index 0000000..ccf4e97 --- /dev/null +++ b/src/filesystem/LICENSE @@ -0,0 +1,36 @@ +Copyright (c) 2016 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +You are under no obligation whatsoever to provide any bug fixes, patches, or +upgrades to the features, functionality or performance of the source code +("Enhancements") to anyone; however, if you choose to make your Enhancements +available either publicly, or directly to the author of this software, without +imposing a separate written license agreement for such Enhancements, then you +hereby grant the following license: a non-exclusive, royalty-free perpetual +license to install, use, modify, prepare derivative works, incorporate into +other computer software, distribute, and sublicense such enhancements or +derivative works thereof, in binary and source code form. diff --git a/src/filesystem/filesystem_mini.h b/src/filesystem/filesystem_mini.h new file mode 100644 index 0000000..5b3e23d --- /dev/null +++ b/src/filesystem/filesystem_mini.h @@ -0,0 +1,211 @@ +#ifndef FILESYSTEM_PATH_H_ +#define FILESYSTEM_PATH_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux) + #include +#endif + +namespace filesystem { + /** + * \brief Simple class for manipulating paths on POSIX systems + */ + class path { + public: + path() : m_absolute(false) {} + path(const path &path) : m_path(path.m_path), m_absolute(path.m_absolute) {} + path(const char *string) { set(string); } + path(const std::string &string) { set(string); } + size_t length() const { return m_path.size(); } + bool empty() const { return m_path.empty(); } + bool is_absolute() const { return m_absolute; } + + path make_absolute() const { + char temp[PATH_MAX]; + + if (realpath(string().c_str(), temp) == NULL) + throw std::runtime_error("Internal error in realpath(): " + std::string(strerror(errno))); + + return path(temp); + } + + bool exists() const { + struct stat sb; + return stat(string().c_str(), &sb) == 0; + } + + size_t file_size() const { + struct stat sb; + + if (stat(string().c_str(), &sb) != 0) + throw std::runtime_error("path::file_size(): cannot stat file \"" + string() + "\"!"); + + return (size_t) sb.st_size; + } + + bool is_directory() const { + struct stat sb; + + if (stat(string().c_str(), &sb)) + return false; + + return S_ISDIR(sb.st_mode); + } + + bool is_file() const { + struct stat sb; + + if (stat(string().c_str(), &sb)) + return false; + + return S_ISREG(sb.st_mode); + } + + std::string extension() const { + const std::string &name = filename(); + size_t pos = name.find_last_of("."); + + if (pos == std::string::npos) + return ""; + + return name.substr(pos+1); + } + + std::string filename() const { + if (empty()) + return ""; + + const std::string &last = m_path[m_path.size()-1]; + return last; + } + + path parent_path() const { + path result; + result.m_absolute = m_absolute; + + if (m_path.empty()) { + if (!m_absolute) + result.m_path.push_back(".."); + } else { + size_t until = m_path.size() - 1; + + for (size_t i = 0; i < until; ++i) + result.m_path.push_back(m_path[i]); + } + + return result; + } + + path& operator/=(const path &other) { + if (other.m_absolute) + throw std::runtime_error("path::operator/(): expected a relative path!"); + + for (size_t i=0; i tokenize(const std::string &string, const std::string &delim) { + std::string::size_type lastPos = 0, pos = string.find_first_of(delim, lastPos); + std::vector tokens; + + while (lastPos != std::string::npos) { + if (pos != lastPos) + tokens.push_back(string.substr(lastPos, pos - lastPos)); + + lastPos = pos; + + if (lastPos == std::string::npos || lastPos + 1 == string.length()) + break; + + pos = string.find_first_of(delim, ++lastPos); + } + + return tokens; + } + + protected: + std::vector m_path; + bool m_absolute; + }; + + inline bool exists(const path& p) { + return p.exists(); + } + + inline bool equivalent(const path& p1, const path& p2) { + return p1.make_absolute() == p2.make_absolute(); + } +} +#endif \ No newline at end of file diff --git a/src/tests/src/main.cpp b/src/tests/src/main.cpp index bae552a..d945166 100644 --- a/src/tests/src/main.cpp +++ b/src/tests/src/main.cpp @@ -75,17 +75,17 @@ int main(int argc, char** argv) log.open(logfile.c_str(), ios::app); log << endl << "==========================" << endl; log << endl << "BEGIN TESTS" << endl << endl; - + // // Try to run all tests specified in the test list file // - try + try { ifstream file(tests_file.c_str()); if(file.fail()) throw runtime_error(string("couldn't open test list file '") + tests_file + "'"); - + while(!file.eof()) { string testfilename; @@ -109,7 +109,7 @@ int main(int argc, char** argv) // // NOTE: this assumes that all the paths in the test list file // are relative, which is fine for our purposes. For anything - // more general, we should just use std::experimental::filesystem::v1::path... + // more general, we should just use the standard library's filesystem path... string test_dir( testfilename ); string::size_type lastsep = test_dir.find_last_of("\\/"); if(lastsep == string::npos) diff --git a/src/util.h b/src/util.h index 924051d..176eccb 100644 --- a/src/util.h +++ b/src/util.h @@ -1,9 +1,10 @@ #pragma once #include -#include #ifdef _WIN32 +#include + inline std::wstring ConvertToNativeString(const std::string str) { std::wstring_convert> converter; std::wstring wstr = converter.from_bytes(str);