diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..da138ba --- /dev/null +++ b/.clang-format @@ -0,0 +1,24 @@ +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: WithoutElse +BasedOnStyle: Microsoft +BreakBeforeBraces: Allman +ColumnLimit: 130 +Cpp11BracedListStyle: true +FixNamespaceComments: true +IncludeBlocks: Regroup +IndentPPDirectives: BeforeHash +IndentWidth: 4 +Language: Cpp +NamespaceIndentation: All +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesInParentheses: false +Standard: c++11 +UseTab: Never \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..933e505 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,21 @@ +name: Build & Test +on: [ push, pull_request ] +jobs: + build: + name: Testing on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, windows-latest, macos-latest ] + steps: + - uses: actions/checkout@v1 + + - name: Configure + run: cmake -DBUILD_TEST=ON . + + - name: Build + run: cmake --build . + + - name: Test + run: ctest -C Debug . diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 27e9248..0000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ - -env: - global: - - CONAN_REFERENCE: "MofileReader/1.0.0" - - CONAN_USERNAME: "anotherfoxguy" - - CONAN_LOGIN_USERNAME: "anotherfoxguy" - - CONAN_CHANNEL: "testing" - - CONAN_UPLOAD: "https://api.bintray.com/conan/anotherfoxguy/ror-dependencies" - -linux: &linux - os: linux - sudo: required - language: python - python: "3.6" - services: - - docker -osx: &osx - os: osx - language: generic -matrix: - include: - - <<: *linux - env: CONAN_GCC_VERSIONS=5 CONAN_DOCKER_IMAGE=conanio/gcc5 - - - <<: *linux - env: CONAN_GCC_VERSIONS=6 CONAN_DOCKER_IMAGE=conanio/gcc6 - - - <<: *linux - env: CONAN_GCC_VERSIONS=7 CONAN_DOCKER_IMAGE=conanio/gcc7 - - - <<: *linux - env: CONAN_GCC_VERSIONS=8 CONAN_DOCKER_IMAGE=conanio/gcc8 - -install: - - chmod +x .travis/install.sh - - ./.travis/install.sh - -script: - - chmod +x .travis/run.sh - - ./.travis/run.sh diff --git a/.travis/install.sh b/.travis/install.sh deleted file mode 100644 index 9da0265..0000000 --- a/.travis/install.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -e -set -x - -if [[ "$(uname -s)" == 'Darwin' ]]; then - brew update || brew update - brew outdated pyenv || brew upgrade pyenv - brew install pyenv-virtualenv - brew install cmake || true - - if which pyenv > /dev/null; then - eval "$(pyenv init -)" - fi - - pyenv install 2.7.10 - pyenv virtualenv 2.7.10 conan - pyenv rehash - pyenv activate conan -fi - -pip install conan --upgrade -pip install conan_package_tools - -conan user diff --git a/.travis/run.sh b/.travis/run.sh deleted file mode 100644 index 0a3488e..0000000 --- a/.travis/run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e -set -x - -if [[ "$(uname -s)" == 'Darwin' ]]; then - if which pyenv > /dev/null; then - eval "$(pyenv init -)" - fi - pyenv activate conan -fi - -python build.py diff --git a/CMakeLists.txt b/CMakeLists.txt index c0d2242..81c9734 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,55 +1,21 @@ #------------------------------------------------------- # moFileReader Main Build Script -# -# Defined Variables: -# - COMPILE_DLL -# - ON : Compiles the code as a shared Library -# - OFF : Compiles the code as a static Library -# - BUILD_DEBUG -# - ON : Compiles Debug-Information into the output -# - OFF : Optimizes the compilation with O2. -# -# Run cmake with -DVARNAME=ON/OFF to benefit from those -# possible settings. #------------------------------------------------------- cmake_minimum_required(VERSION 3.0) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_CXX_STANDARD 11) project(moFileReader) -# Let the user choose between static lib and dll -# To use it, call cmake -DCOMPILE_DLL=ON -option(COMPILE_DLL "Set this to ON if you want to compile the library as an DLL. When this is OFF, a static library is created (default)." OFF) -if (COMPILE_DLL) - # DLL - target_compile_definitions(moReader PRIVATE _USRDLL MOFILE_EXPORTS) -endif () - -add_library(moFileReader STATIC ${CMAKE_SOURCE_DIR}/src/moFileReader.cpp ${CMAKE_SOURCE_DIR}/src/mo.cpp) add_executable(moReader ${CMAKE_SOURCE_DIR}/src/mo.cpp) - -target_include_directories(moFileReader PRIVATE ${CMAKE_SOURCE_DIR}/include) target_include_directories(moReader PRIVATE ${CMAKE_SOURCE_DIR}/include) -if (COMPILE_DLL) - target_compile_definitions(moReader PRIVATE _CONSOLE MOFILE_IMPORT) -else () - target_compile_definitions(moReader PRIVATE _CONSOLE) -endif () - -add_dependencies(moReader moFileReader) -target_link_libraries(moReader moFileReader) - install(TARGETS moReader RUNTIME DESTINATION bin ) -install(TARGETS moFileReader - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - PUBLIC_HEADER DESTINATION include -) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/) option(BUILD_TEST "Set this to ON if you want to build the test" OFF) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 0f3730e..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,30 +0,0 @@ -build: false - -environment: - PYTHON: "C:\\Python37" - - CONAN_REFERENCE: "MofileReader/1.0.0" - CONAN_USERNAME: "anotherfoxguy" - CONAN_LOGIN_USERNAME: "anotherfoxguy" - CONAN_CHANNEL: "testing" - CONAN_UPLOAD: "https://api.bintray.com/conan/anotherfoxguy/ror-dependencies" - - matrix: - - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - CONAN_VISUAL_VERSIONS: 16 - CONAN_BUILD_TYPES: Release - CONAN_ARCHS: x86_64 - - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - CONAN_VISUAL_VERSIONS: 16 - CONAN_BUILD_TYPES: Debug - CONAN_ARCHS: x86_64 - - -install: - - set PATH=%PATH%;%PYTHON%/Scripts/ - - pip.exe install conan --upgrade - - pip.exe install conan_package_tools - - conan user # It creates the conan data directory - -test_script: - - python build.py diff --git a/build.py b/build.py deleted file mode 100644 index 64e8245..0000000 --- a/build.py +++ /dev/null @@ -1,7 +0,0 @@ -from conan.packager import ConanMultiPackager - - -if __name__ == "__main__": - builder = ConanMultiPackager() - builder.add_common_builds() - builder.run() diff --git a/cmake/FindCatch2.cmake b/cmake/FindCatch2.cmake new file mode 100644 index 0000000..d51a57c --- /dev/null +++ b/cmake/FindCatch2.cmake @@ -0,0 +1,17 @@ +set(Catch2_FOUND TRUE) + +set(CATCH2_VERSION "v2.13.4") +set(CATCH2_INCLUDEDIR "${CMAKE_BINARY_DIR}/catch-${CATCH2_VERSION}") +list(APPEND CMAKE_MODULE_PATH "${CATCH2_INCLUDEDIR}") + +if (NOT EXISTS "${CATCH2_INCLUDEDIR}/catch.hpp") + file(MAKE_DIRECTORY "${CATCH2_INCLUDEDIR}") + message(STATUS "Downloading catch.hpp from https://github.com/catchorg/Catch2/") + file(DOWNLOAD "https://github.com/catchorg/Catch2/releases/download/${CATCH2_VERSION}/catch.hpp" "${CATCH2_INCLUDEDIR}/catch.hpp") + file(DOWNLOAD "https://cdn.statically.io/gh/catchorg/Catch2/${CATCH2_VERSION}/contrib/Catch.cmake" "${CATCH2_INCLUDEDIR}/Catch.cmake") + file(DOWNLOAD "https://cdn.statically.io/gh/catchorg/Catch2/${CATCH2_VERSION}/contrib/CatchAddTests.cmake" "${CATCH2_INCLUDEDIR}/CatchAddTests.cmake") + file(DOWNLOAD "https://cdn.statically.io/gh/catchorg/Catch2/${CATCH2_VERSION}/contrib/ParseAndAddCatchTests.cmake" "${CATCH2_INCLUDEDIR}/ParseAndAddCatchTests.cmake") +endif () + +add_library(Catch2::Catch2 INTERFACE IMPORTED) +set_target_properties(Catch2::Catch2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CATCH2_INCLUDEDIR}") \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index 94b6d5e..8e46ab8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,24 +1,14 @@ -from conans import ConanFile, CMake, tools -import os +from conans import ConanFile + class MofilereaderConan(ConanFile): name = "MofileReader" - version = "1.0.0" + version = "1.1.0" license = "MIT" - url = "https://github.com/AnotherFoxGuy/conan-MofileReader/" + url = "https://github.com/AnotherFoxGuy/MofileReader/" description = "This API lets you read .mo-Files and use their content just as you would do with GNUs gettext." settings = "os", "compiler", "build_type", "arch" - exports_sources = "include*", "src*", "CMakeLists.txt" - - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() + exports_sources = "include*" def package(self): - cmake = CMake(self) - cmake.install() - - def package_info(self): - self.cpp_info.libs = tools.collect_libs(self) + self.copy("*.hpp", "include", "include") diff --git a/include/moFileConfig.h b/include/moFileConfig.h deleted file mode 100644 index 32479dc..0000000 --- a/include/moFileConfig.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * moFileReader - A simple .mo-File-Reader - * Copyright (C) 2009 Domenico Gentner (scorcher24@gmail.com) - * Copyright (C) 2018 Edgar (Edgar@AnotherFoxGuy.com) - * 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. The names of its contributors may not 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 OWNER 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. - */ -#ifndef __MOFILECONFIG_H_INCLUDED__ -#define __MOFILECONFIG_H_INCLUDED__ - -//------------------------------------------------------------- -// Defines an export-macro when compiling as dll on woe32. -//------------------------------------------------------------- -#if defined(MOFILE_EXPORTS) && defined (WIN32) -# define MOEXPORT __declspec(dllexport) -#elif defined (MOFILE_IMPORT) && defined(WIN32) -# define MOEXPORT __declspec(dllimport) -#else -# define MOEXPORT -#endif - -//------------------------------------------------------------- -// Path-Seperators are different on other OS. -//------------------------------------------------------------- -#ifdef WIN32 -# define moPATHSEP std::string("\\") -#else -# define moPATHSEP std::string("/") -#endif - -//------------------------------------------------------------- -// Defines the beginning of the namespace moFileLib. -//------------------------------------------------------------- -#define MO_BEGIN_NAMESPACE namespace moFileLib{ - -//------------------------------------------------------------- -// Ends the current namespace. -//------------------------------------------------------------- -#define MO_END_NAMESPACE } - - - -#endif /* __MOFILECONFIG_H_INCLUDED__ */ diff --git a/include/moFileReader.h b/include/moFileReader.h deleted file mode 100644 index 47eb826..0000000 --- a/include/moFileReader.h +++ /dev/null @@ -1,477 +0,0 @@ -/* - * moFileReader - A simple .mo-File-Reader - * Copyright (C) 2009 Domenico Gentner (scorcher24@gmail.com) - * Copyright (C) 2018 Edgar (Edgar@AnotherFoxGuy.com) - * 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. The names of its contributors may not 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 OWNER 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. - */ -#ifndef __MOFILEREADER_H_INCLUDED__ -#define __MOFILEREADER_H_INCLUDED__ - -#include -#include -#include -#include // this is for memset when compiling with gcc. -#include -#include - -#ifndef __MOFILECONFIG_H_INCLUDED__ -# include "moFileConfig.h" -#endif - -/** \mainpage moFileReaderSDK - * - *

Compilation via cmake

- * - * - Make sure you have cmake installed and in your path. If not, go to http://www.cmake.org and get it. - * - Switch to a Shell or commandline - * - Run cmake in $INSTALLDIR\\build. See cmake --help for possible generators. Here are the available Options: - * - COMPILE_DLL Setting this to ON will compile the library as a shared module. By default, a static library is built. - * . - * Example: - * \code - * cmake -G"MinGW Makefiles" -DCOMPILE_DLL=ON - * cmake -G"Visual Studio 9 2008" - * // etc - * \endcode - * - * cmake will compile the library and moReader[.exe]-binary, which can do lookups in moFiles and export moFiles as HTML. - * See moReader[.exe] --help for details. - * You will find the libraries in %%projectdir%%/lib and the binary in %%projectdir%%/bin - * - * - *

Include in project

- * - * The last option is to simply add moFileReader.cpp, moFileReader.h and moFileConfig.h to your project. Thats all you have to do. - * You can safely exclude mo.cpp, since this file keeps the entry-points of the .exe and .dll only. - * - *

Usage

- * - * This is moFileReader, a simple gettext-replacement. The usage of this library is, hopefully, fairly simple: - * \code - * - * // Instanciate the class - * moFileLib::moFileReader reader; - * - * // Load a .mo-File. - * if ( reader.ReadFile("myTranslationFile.mo") != moFileLib::moFileReader::EC_SUCCESS ) - * { - * // Error Handling - * } - * - * // Now, you can lookup the strings you stored in the .mo-File: - * std::cout << reader.Lookup("MyTranslationString") << std::endl; - * - * \endcode - * Thats all! This small code has no dependencies, except the C/C++-runtime of your compiler, - * so it should work on all machines where a C++-runtime is provided. - * - * \note We do not yet support .mo-Files with reversed magic-numbers, since I don't have - * a file to test it and I hate to release stuff I wasn't able to test. - * - *

Changelog

- * - * - Version 1.0.0 - * - Added new function: LookupWithContext - * - Added unit-tests - * - Added support for packaging with Conan - * - Moved project to https://github.com/AnotherFoxGuy/MofileReader - * - * - Version 0.1.2 - * - Generic improvements to the documentation. - * - Generic improvements to the code - * - Fixed a bug in mo.cpp which caused the application not to print the help - * message if only --export or --lookup where missing. - * - Added -h, --help and -? to moReader[.exe]. It will print the help-screen. - * - Added --version and -v to moReader[.exe]. It will print some informations about the program. - * - Added --license to moReader[.exe]. This will print its license. - * - --export gives now a feedback about success or failure. - * - The HTML-Dump-Method outputs now the whole table from the empty msgid in a nice html-table, not only a few hardcoded. - * - I had an issue-report that the Error-Constants can collide with foreign code under certain conditions, - * so I added a patch which renamed the error-constants to more compatible names. - * - * - Version 0.1.1 - * - Added the ability to export mo's as HTML. - * - Fixed a bug causing a crash when passing an invalid value to moFileReader::Lookup(). - * - Added a new file, moFileConfig.h, holding the macros for the project. - * - Added the ability to be configured by cmake. - * - Added some more inline-functions, which really enhance the singleton. - * - * - Version 0.1.0 - * - Initial Version and release to http://googlecode.com - * - * - *

Credits

- * - * Gettext is part of the GNU-Tools and (C) by the Free Software Foundation.\n - * Visual C++ Express is a registered Trademark of Microsoft, One Microsoft Way, Redmond, USA.\n - * moFileReader is using NSIS for creating the setup-package. \n - * All other Trademarks are property of their respective owners. \n - * \n - * Thanks for using this piece of OpenSource-Software.\n - * Submit patches and/or bugs on https://github.com/AnotherFoxGuy/MofileReader. - * Send your flames, dumb comments etc to /dev/null, thank you. - */ - - - -/* - About Warning 4251: - http://support.microsoft.com/default.aspx?scid=KB;EN-US;16. - - I am aware of this warning and know how to deal with it. - To avoid that derived projects are influenced by this warning - I have deactivated it for your convinience. - Note: This warning only occurs, when using this code as a DLL. -*/ -#if defined(_MSC_VER) && ( defined(_EXPORT) || defined(MOFILE_IMPORT) ) -# pragma warning (disable:4251) -#endif /* _MSC_VER */ - - -/** \namespace moFileLib - * \brief This is the only namespace of this small sourcecode. - */ -MO_BEGIN_NAMESPACE - -const std::string g_css = \ -"\ -body {\ - background-color: black;\ - color: silver;\ -}\ -table {\ -width: 80%;}\ -th {\ -background-color: orange;\ -color: black;\ -}\ -hr { color: red;width: 80%; size: 5px; }\ -a:link{color: gold;}\ -a:visited{color: grey;}\ -a:hover{color:blue;}\ -.copyleft{\ - font-size: 12px; \ - text-align: center;\ -}\ -"; - -/** - * \brief Keeps the Description of translated and original strings. - * - * - * To load a String from the file, we need its offset and its length. - * This struct helps us grouping this information. - */ -struct moTranslationPairInformation -{ - /// \brief Constructor - moTranslationPairInformation() - : m_orLength(0), m_orOffset(0), - m_trLength(0), m_trOffset(0) - {} - - /// \brief Length of the Original String - int m_orLength; - - /// \brief Offset of the Original String (absolute) - int m_orOffset; - - /// \brief Length of the Translated String - int m_trLength; - - /// \brief Offset of the Translated String (absolute) - int m_trOffset; -}; - -/** - * \brief Describes the "Header" of a .mo-File. - * - * - * The File info keeps the header of a .mo-file and - * a list of the string-descriptions. - * The typedef is for the type of the string-list. - * The constructor ensures, that all members get a nice - * initial value. - */ -struct moFileInfo -{ - /// \brief Type for the list of all Translation-Pair-Descriptions. - typedef std::deque moTranslationPairList; - - /// \brief Constructor - moFileInfo() - : m_magicNumber(0), m_fileVersion(0), m_numStrings(0), - m_offsetOriginal(0), m_offsetTranslation(0), m_sizeHashtable(0), - m_offsetHashtable(0), m_reversed(false) - {} - - /// \brief The Magic Number, compare it to g_MagicNumber. - int m_magicNumber; - - /// \brief The File Version, 0 atm according to the manpage. - int m_fileVersion; - - /// \brief Number of Strings in the .mo-file. - int m_numStrings; - - /// \brief Offset of the Table of the Original Strings - int m_offsetOriginal; - - /// \brief Offset of the Table of the Translated Strings - int m_offsetTranslation; - - /// \brief Size of 1 Entry in the Hashtable. - int m_sizeHashtable; - - /// \brief The Offset of the Hashtable. - int m_offsetHashtable; - - /** \brief Tells you if the bytes are reversed - * \note When this is true, the bytes are reversed and the Magic number is like g_MagicReversed - */ - bool m_reversed; - - /// \brief A list containing offset and length of the strings in the file. - moTranslationPairList m_translationPairInformation; -}; - -/** - * \brief This class is a gettext-replacement. - * - * - * The usage is quite simple:\n - * Tell the class which .mo-file it shall load via - * moFileReader::ReadFile(). The method will attempt to load - * the file, all translations will be stored in memory. - * Afterwards you can lookup the strings with moFileReader::Lookup() just - * like you would do with gettext. - * Additionally, you can call moFileReader::ReadFile() for as much files as you - * like. But please be aware, that if there are duplicated keys (original strings), - * that they will replace each other in the lookup-table. There is no check done, if a - * key already exists. - * - * \note If you add "Lookup" to the keywords of the gettext-parser (like poEdit), - * it will recognize the Strings loaded with an instance of this class. - * \note I strongly recommend poEdit from Vaclav Slavik for editing .po-Files, - * get it at http://poedit.net for various systems :). - */ -class MOEXPORT moFileReader -{ -protected: - /// \brief Type for the map which holds the translation-pairs later. - typedef std::map moLookupList; - -public: - - /// \brief The Magic Number describes the endianess of bytes on the system. - static const long MagicNumber = 0x950412DE; - - /// \brief If the Magic Number is Reversed, we need to swap the bytes. - static const long MagicReversed = 0xDE120495; - - /// \brief The possible errorcodes for methods of this class - enum eErrorCode - { - /// \brief Indicated success - EC_SUCCESS = 0, - - /// \brief Indicates an error - EC_ERROR, - - /// \brief The given File was not found. - EC_FILENOTFOUND, - - /// \brief The file is invalid. - EC_FILEINVALID, - - /// \brief Empty Lookup-Table (returned by ExportAsHTML()) - EC_TABLEEMPTY, - - /// \brief The magic number did not match - EC_MAGICNUMBER_NOMATCH, - - /** - * \brief The magic number is reversed. - * \note This is an error until the class supports it. - */ - EC_MAGICNUMBER_REVERSED, - }; - - /** \brief Reads a .mo-file - * \param[in] _filename The path to the file to load. - * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. - * - * This is the core-feature. This method loads the .mo-file and stores - * all translation-pairs in a map. You can access this map via the method - * moFileReader::Lookup(). - */ - virtual moFileReader::eErrorCode ParseData(std::string data); - - /** \brief Reads a .mo-file - * \param[in] _filename The path to the file to load. - * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. - * - * This is the core-feature. This method loads the .mo-file and stores - * all translation-pairs in a map. You can access this map via the method - * moFileReader::Lookup(). - */ - virtual eErrorCode ReadFile(const char* filename); - - /** \brief Returns the searched translation or returns the input. - * \param[in] id The id of the translation to search for. - * \return The value you passed in via _id or the translated string. - */ - virtual std::string Lookup( const char* id ) const; - - /** \brief Returns the searched translation or returns the input, restricted to the context given by context. - * See https://www.gnu.org/software/gettext/manual/html_node/Contexts.html for more info. - * \param[in] context Restrict to the context given. - * \param[in] id The id of the translation to search for. - * \return The value you passed in via _id or the translated string. - */ - virtual std::string LookupWithContext (const char* context, const char* id) const; - - - /// \brief Returns the Error Description. - virtual const std::string& GetErrorDescription() const; - - /// \brief Empties the Lookup-Table. - virtual void ClearTable(); - - /** \brief Returns the Number of Entries in our Lookup-Table. - * \note The mo-File-table always contains an empty msgid, which contains informations - * about the tranlsation-project. So the real number of strings is always minus 1. - */ - virtual unsigned int GetNumStrings() const; - - /** \brief Exports the whole content of the .mo-File as .html - * \param[in] infile The .mo-File to export. - * \param[in] filename Where to store the .html-file. If empty, the path and filename of the _infile with .html appended. - * \param[in,out] css The css-script for the visual style of the - * file, in case you don't like mine ;). - * \see g_css for the possible and used css-values. - */ - static eErrorCode ExportAsHTML(const std::string infile, const std::string filename = "", const std::string css = g_css ); - -protected: - /// \brief Keeps the last error as String. - std::string m_error; - - /** \brief Swap the endianness of a 4 byte WORD. - * \param[in] in The value to swap. - * \return The swapped value. - */ - unsigned long SwapBytes(unsigned long in); - -private: - // Holds the lookup-table - moLookupList m_lookup; - - void MakeHtmlConform(std::string& _inout); - bool GetPoEditorString(const char* _buffer, std::string& _name, std::string& _value); - void Trim(std::string& _in); -}; - -/** \brief Convience Class - * - * - * This class derives from moFileReader and builds a singleton to access its methods - * in a global manner. - * \note This class is a Singleton. Please access it via moFileReaderSingleton::GetInstance() - * or use the provided wrappers:\n - * - moReadMoFile() - * - _() - * - moFileClearTable() - * - moFileGetErrorDescription() - * - moFileGetNumStrings(); - */ -class MOEXPORT moFileReaderSingleton : public moFileReader -{ -private: - // Private Contructor and Copy-Constructor to avoid - // that this class is instanced. - moFileReaderSingleton(); - moFileReaderSingleton(const moFileReaderSingleton&); - moFileReaderSingleton& operator=(const moFileReaderSingleton&); - -public: - /** \brief Singleton-Accessor. - * \return A static instance of moFileReaderSingleton. - */ - static moFileReaderSingleton& GetInstance(); -}; - -/** \brief Reads the .mo-File. - * \param[in] _filename The path to the file to use. - * \see moFileReader::ReadFile() for details. - */ -inline moFileReader::eErrorCode moReadMoFile(const char* _filename) -{ - moFileReader::eErrorCode r = moFileReaderSingleton::GetInstance().ReadFile(_filename); - return r; -} - -/** \brief Looks for the spec. string to translate. - * \param[in] id The string-id to search. - * \return The translation if found, otherwise it returns id. - */ -inline std::string _(const char* id) -{ - std::string r = moFileReaderSingleton::GetInstance().Lookup(id); - return r; -} - -/// \brief Resets the Lookup-Table. -inline void moFileClearTable() -{ - moFileReaderSingleton::GetInstance().ClearTable(); -} - -/// \brief Returns the last known error as string or an empty class. -inline std::string moFileGetErrorDescription() -{ - std::string r = moFileReaderSingleton::GetInstance().GetErrorDescription(); - return r; -} - -/// \brief Returns the number of entries loaded from the .mo-File. -inline int moFileGetNumStrings() -{ - int r = moFileReaderSingleton::GetInstance().GetNumStrings(); - return r; -} - -#if defined(_MSC_VER) -# pragma warning (default:4251) -#endif /* _MSC_VER */ - -MO_END_NAMESPACE - -#endif /* __MOFILEREADER_H_INCLUDED__ */ diff --git a/include/moFileReader.hpp b/include/moFileReader.hpp new file mode 100644 index 0000000..f90132e --- /dev/null +++ b/include/moFileReader.hpp @@ -0,0 +1,901 @@ +/* + * moFileReader - A simple .mo-File-Reader + * Copyright (C) 2009 Domenico Gentner (scorcher24@gmail.com) + * Copyright (C) 2018-2021 Edgar (Edgar@AnotherFoxGuy.com) + * 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. The names of its contributors may not 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 OWNER 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. + */ +#ifndef __MOFILEREADER_SINGLE_INCLUDE_H_INCLUDED__ +#define __MOFILEREADER_SINGLE_INCLUDE_H_INCLUDED__ + +#if defined(_MSC_VER) + #pragma warning(disable : 4267) +#endif /* _MSC_VER */ + +#include // this is for memset when compiling with gcc. +#include +#include +#include +#include +#include + +//------------------------------------------------------------- +// Path-Seperators are different on other OS. +//------------------------------------------------------------- +#ifndef moPATHSEP + #ifdef WIN32 + #define moPATHSEP std::string("\\") + #else + #define moPATHSEP std::string("/") + #endif +#endif + +//------------------------------------------------------------- +// Defines the beginning of the namespace moFileLib. +//------------------------------------------------------------- +#ifndef MO_BEGIN_NAMESPACE + #define MO_BEGIN_NAMESPACE \ + namespace moFileLib \ + { +#endif +//------------------------------------------------------------- +// Ends the current namespace. +//------------------------------------------------------------- +#ifndef MO_END_NAMESPACE + #define MO_END_NAMESPACE } +#endif + +/** \mainpage moFileReaderSDK + * + * + *

Include in project

+ * + * Usage of this library is quite easy, simply add moFileReader.hpp to your project. Thats all you have to do. + * You can safely exclude mo.cpp, since this file keeps the entry-points of the .exe only. + * + *

Usage

+ * + * This is moFileReader, a simple gettext-replacement. The usage of this library is, hopefully, fairly simple: + * \code + * + * // Instanciate the class + * moFileLib::moFileReader reader; + * + * // Load a .mo-File. + * if ( reader.ReadFile("myTranslationFile.mo") != moFileLib::moFileReader::EC_SUCCESS ) + * { + * // Error Handling + * } + * + * // Now, you can lookup the strings you stored in the .mo-File: + * std::cout << reader.Lookup("MyTranslationString") << std::endl; + * + * \endcode + * Thats all! This small code has no dependencies, except the C/C++-runtime of your compiler, + * so it should work on all machines where a C++-runtime is provided. + * + * \note We do not yet support .mo-Files with reversed magic-numbers, since I don't have + * a file to test it and I hate to release stuff I wasn't able to test. + * + *

Changelog

+ * + * - Version 1.1.0 + * - Converted library to a header-only library + * + * - Version 1.0.0 + * - Added new function: LookupWithContext + * - Added unit-tests + * - Added support for packaging with Conan + * - Moved project to https://github.com/AnotherFoxGuy/MofileReader + * + * - Version 0.1.2 + * - Generic improvements to the documentation. + * - Generic improvements to the code + * - Fixed a bug in mo.cpp which caused the application not to print the help + * message if only --export or --lookup where missing. + * - Added -h, --help and -? to moReader[.exe]. It will print the help-screen. + * - Added --version and -v to moReader[.exe]. It will print some informations about the program. + * - Added --license to moReader[.exe]. This will print its license. + * - --export gives now a feedback about success or failure. + * - The HTML-Dump-Method outputs now the whole table from the empty msgid in a nice html-table, not only a few hardcoded. + * - I had an issue-report that the Error-Constants can collide with foreign code under certain conditions, + * so I added a patch which renamed the error-constants to more compatible names. + * + * - Version 0.1.1 + * - Added the ability to export mo's as HTML. + * - Fixed a bug causing a crash when passing an invalid value to moFileReader::Lookup(). + * - Added a new file, moFileConfig.h, holding the macros for the project. + * - Added the ability to be configured by cmake. + * - Added some more inline-functions, which really enhance the singleton. + * + * - Version 0.1.0 + * - Initial Version and release to http://googlecode.com + * + * + *

Credits

+ * + * Gettext is part of the GNU-Tools and (C) by the Free Software Foundation.\n + * Visual C++ Express is a registered Trademark of Microsoft, One Microsoft Way, Redmond, USA.\n + * moFileReader is using NSIS for creating the setup-package. \n + * All other Trademarks are property of their respective owners. \n + * \n + * Thanks for using this piece of OpenSource-Software.\n + * Submit patches and/or bugs on https://github.com/AnotherFoxGuy/MofileReader. + * Send your flames, dumb comments etc to /dev/null, thank you. + */ + +/** \namespace moFileLib + * \brief This is the only namespace of this small sourcecode. + */ +MO_BEGIN_NAMESPACE + +const std::string g_css = R"( +body { + background-color: black; + color: silver; +} +table { + width: 80%; +} +th { + background-color: orange; + color: black; +} +hr { + color: red; + width: 80%; + size: 5px; +} +a:link{ + color: gold; +} +a:visited{ + color: grey; +} +a:hover{ + color:blue; +} +.copyleft{ + font-size: 12px; + text-align: center; +})"; + +/** + * \brief Keeps the Description of translated and original strings. + * + * + * To load a String from the file, we need its offset and its length. + * This struct helps us grouping this information. + */ +struct moTranslationPairInformation +{ + /// \brief Constructor + moTranslationPairInformation() : m_orLength(0), m_orOffset(0), m_trLength(0), m_trOffset(0) + { + } + + /// \brief Length of the Original String + int m_orLength; + + /// \brief Offset of the Original String (absolute) + int m_orOffset; + + /// \brief Length of the Translated String + int m_trLength; + + /// \brief Offset of the Translated String (absolute) + int m_trOffset; +}; + +/** + * \brief Describes the "Header" of a .mo-File. + * + * + * The File info keeps the header of a .mo-file and + * a list of the string-descriptions. + * The typedef is for the type of the string-list. + * The constructor ensures, that all members get a nice + * initial value. + */ +struct moFileInfo +{ + /// \brief Type for the list of all Translation-Pair-Descriptions. + typedef std::deque moTranslationPairList; + + /// \brief Constructor + moFileInfo() + : m_magicNumber(0), m_fileVersion(0), m_numStrings(0), m_offsetOriginal(0), m_offsetTranslation(0), m_sizeHashtable(0), + m_offsetHashtable(0), m_reversed(false) + { + } + + /// \brief The Magic Number, compare it to g_MagicNumber. + int m_magicNumber; + + /// \brief The File Version, 0 atm according to the manpage. + int m_fileVersion; + + /// \brief Number of Strings in the .mo-file. + int m_numStrings; + + /// \brief Offset of the Table of the Original Strings + int m_offsetOriginal; + + /// \brief Offset of the Table of the Translated Strings + int m_offsetTranslation; + + /// \brief Size of 1 Entry in the Hashtable. + int m_sizeHashtable; + + /// \brief The Offset of the Hashtable. + int m_offsetHashtable; + + /** \brief Tells you if the bytes are reversed + * \note When this is true, the bytes are reversed and the Magic number is like g_MagicReversed + */ + bool m_reversed; + + /// \brief A list containing offset and length of the strings in the file. + moTranslationPairList m_translationPairInformation; +}; + +/** + * \brief This class is a gettext-replacement. + * + * + * The usage is quite simple:\n + * Tell the class which .mo-file it shall load via + * moFileReader::ReadFile(). The method will attempt to load + * the file, all translations will be stored in memory. + * Afterwards you can lookup the strings with moFileReader::Lookup() just + * like you would do with gettext. + * Additionally, you can call moFileReader::ReadFile() for as much files as you + * like. But please be aware, that if there are duplicated keys (original strings), + * that they will replace each other in the lookup-table. There is no check done, if a + * key already exists. + * + * \note If you add "Lookup" to the keywords of the gettext-parser (like poEdit), + * it will recognize the Strings loaded with an instance of this class. + * \note I strongly recommend poEdit from Vaclav Slavik for editing .po-Files, + * get it at http://poedit.net for various systems :). + */ +class moFileReader +{ + protected: + /// \brief Type for the map which holds the translation-pairs later. + typedef std::map moLookupList; + + public: + /// \brief The Magic Number describes the endianess of bytes on the system. + static const long MagicNumber = 0x950412DE; + + /// \brief If the Magic Number is Reversed, we need to swap the bytes. + static const long MagicReversed = 0xDE120495; + + /// \brief The possible errorcodes for methods of this class + enum eErrorCode + { + /// \brief Indicated success + EC_SUCCESS = 0, + + /// \brief Indicates an error + EC_ERROR, + + /// \brief The given File was not found. + EC_FILENOTFOUND, + + /// \brief The file is invalid. + EC_FILEINVALID, + + /// \brief Empty Lookup-Table (returned by ExportAsHTML()) + EC_TABLEEMPTY, + + /// \brief The magic number did not match + EC_MAGICNUMBER_NOMATCH, + + /** + * \brief The magic number is reversed. + * \note This is an error until the class supports it. + */ + EC_MAGICNUMBER_REVERSED, + }; + + /** \brief Reads a .mo-file + * \param[in] _filename The path to the file to load. + * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. + * + * This is the core-feature. This method loads the .mo-file and stores + * all translation-pairs in a map. You can access this map via the method + * moFileReader::Lookup(). + */ + moFileReader::eErrorCode ParseData(const std::string &data) + { + // Creating a file-description. + moFileInfo moInfo; + + // Reference to the List inside moInfo. + moFileInfo::moTranslationPairList &TransPairInfo = moInfo.m_translationPairInformation; + + // Opening the file. + std::stringstream stream(data); + + // Read in all the 4 bytes of fire-magic, offsets and stuff... + stream.read((char *)&moInfo.m_magicNumber, 4); + stream.read((char *)&moInfo.m_fileVersion, 4); + stream.read((char *)&moInfo.m_numStrings, 4); + stream.read((char *)&moInfo.m_offsetOriginal, 4); + stream.read((char *)&moInfo.m_offsetTranslation, 4); + stream.read((char *)&moInfo.m_sizeHashtable, 4); + stream.read((char *)&moInfo.m_offsetHashtable, 4); + + if (stream.bad()) + { + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + printf("%s", m_error.c_str()); + return moFileReader::EC_FILEINVALID; + } + + // Checking the Magic Number + if (MagicNumber != moInfo.m_magicNumber) + { + if (MagicReversed != moInfo.m_magicNumber) + { + m_error = "The Magic Number does not match in all cases!"; + printf("%s", m_error.c_str()); + return moFileReader::EC_MAGICNUMBER_NOMATCH; + } + else + { + moInfo.m_reversed = true; + m_error = "Magic Number is reversed. We do not support this yet!"; + return moFileReader::EC_MAGICNUMBER_REVERSED; + } + } + + // Now we search all Length & Offsets of the original strings + for (int i = 0; i < moInfo.m_numStrings; i++) + { + moTranslationPairInformation _str; + stream.read((char *)&_str.m_orLength, 4); + stream.read((char *)&_str.m_orOffset, 4); + if (stream.bad()) + { + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + printf("%s", m_error.c_str()); + return moFileReader::EC_FILEINVALID; + } + + TransPairInfo.push_back(_str); + } + + // Get all Lengths & Offsets of the translated strings + // Be aware: The Descriptors already exist in our list, so we just mod. refs from the deque. + for (int i = 0; i < moInfo.m_numStrings; i++) + { + moTranslationPairInformation &_str = TransPairInfo[i]; + stream.read((char *)&_str.m_trLength, 4); + stream.read((char *)&_str.m_trOffset, 4); + if (stream.bad()) + { + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + printf("%s", m_error.c_str()); + return moFileReader::EC_FILEINVALID; + } + } + + // Normally you would read the hash-table here, but we don't use it. :) + + // Now to the interesting part, we read the strings-pairs now + for (int i = 0; i < moInfo.m_numStrings; i++) + { + // We need a length of +1 to catch the trailing \0. + int orLength = TransPairInfo[i].m_orLength + 1; + int trLength = TransPairInfo[i].m_trLength + 1; + + int orOffset = TransPairInfo[i].m_orOffset; + int trOffset = TransPairInfo[i].m_trOffset; + + // Original + char *original = new char[orLength]; + memset(original, 0, sizeof(char) * orLength); + + stream.seekg(orOffset); + stream.read(original, orLength); + + if (stream.bad()) + { + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + printf("%s", m_error.c_str()); + return moFileReader::EC_FILEINVALID; + } + + // Translation + char *translation = new char[trLength]; + memset(translation, 0, sizeof(char) * trLength); + + stream.seekg(trOffset); + stream.read(translation, trLength); + + if (stream.bad()) + { + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + printf("%s", m_error.c_str()); + return moFileReader::EC_FILEINVALID; + } + + // Store it in the map. + m_lookup[std::string(original)] = std::string(translation); + + // Cleanup... + delete[] original; + delete[] translation; + } + + // Done :) + return moFileReader::EC_SUCCESS; + } + + /** \brief Reads a .mo-file + * \param[in] _filename The path to the file to load. + * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. + * + * This is the core-feature. This method loads the .mo-file and stores + * all translation-pairs in a map. You can access this map via the method + * moFileReader::Lookup(). + */ + eErrorCode ReadFile(const char *filename) + { + // Creating a file-description. + moFileInfo moInfo; + + // Reference to the List inside moInfo. + moFileInfo::moTranslationPairList &TransPairInfo = moInfo.m_translationPairInformation; + + // Opening the file. + std::ifstream stream(filename, std::ios_base::binary | std::ios_base::in); + if (!stream.is_open()) + { + m_error = std::string("Cannot open File ") + std::string(filename); + return moFileReader::EC_FILENOTFOUND; + } + + // Read in all the 4 bytes of fire-magic, offsets and stuff... + stream.read((char *)&moInfo.m_magicNumber, 4); + stream.read((char *)&moInfo.m_fileVersion, 4); + stream.read((char *)&moInfo.m_numStrings, 4); + stream.read((char *)&moInfo.m_offsetOriginal, 4); + stream.read((char *)&moInfo.m_offsetTranslation, 4); + stream.read((char *)&moInfo.m_sizeHashtable, 4); + stream.read((char *)&moInfo.m_offsetHashtable, 4); + + if (stream.bad()) + { + stream.close(); + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + // Checking the Magic Number + if (MagicNumber != moInfo.m_magicNumber) + { + if (MagicReversed != moInfo.m_magicNumber) + { + m_error = "The Magic Number does not match in all cases!"; + // return moFileReader::EC_MAGICNUMBER_NOMATCH; + } + else + { + moInfo.m_reversed = true; + m_error = "Magic Number is reversed. We do not support this yet!"; + return moFileReader::EC_MAGICNUMBER_REVERSED; + } + } + + // Now we search all Length & Offsets of the original strings + for (int i = 0; i < moInfo.m_numStrings; i++) + { + moTranslationPairInformation _str; + stream.read((char *)&_str.m_orLength, 4); + stream.read((char *)&_str.m_orOffset, 4); + if (stream.bad()) + { + stream.close(); + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + TransPairInfo.push_back(_str); + } + + // Get all Lengths & Offsets of the translated strings + // Be aware: The Descriptors already exist in our list, so we just mod. refs from the deque. + for (int i = 0; i < moInfo.m_numStrings; i++) + { + moTranslationPairInformation &_str = TransPairInfo[i]; + stream.read((char *)&_str.m_trLength, 4); + stream.read((char *)&_str.m_trOffset, 4); + if (stream.bad()) + { + stream.close(); + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + return moFileReader::EC_FILEINVALID; + } + } + + // Normally you would read the hash-table here, but we don't use it. :) + + // Now to the interesting part, we read the strings-pairs now + for (int i = 0; i < moInfo.m_numStrings; i++) + { + // We need a length of +1 to catch the trailing \0. + int orLength = TransPairInfo[i].m_orLength + 1; + int trLength = TransPairInfo[i].m_trLength + 1; + + int orOffset = TransPairInfo[i].m_orOffset; + int trOffset = TransPairInfo[i].m_trOffset; + + // Original + char *original = new char[orLength]; + memset(original, 0, sizeof(char) * orLength); + + stream.seekg(orOffset); + stream.read(original, orLength); + + if (stream.bad()) + { + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + // Translation + char *translation = new char[trLength]; + memset(translation, 0, sizeof(char) * trLength); + + stream.seekg(trOffset); + stream.read(translation, trLength); + + if (stream.bad()) + { + m_error = "Stream bad during reading. The .mo-file seems to be invalid or has bad descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + // Store it in the map. + m_lookup[std::string(original)] = std::string(translation); + + // Cleanup... + delete[] original; + delete[] translation; + } + + // Done :) + stream.close(); + return moFileReader::EC_SUCCESS; + } + + /** \brief Returns the searched translation or returns the input. + * \param[in] id The id of the translation to search for. + * \return The value you passed in via _id or the translated string. + */ + std::string Lookup(const char *id) const + { + if (m_lookup.empty()) return id; + auto iterator = m_lookup.find(id); + + if (iterator == m_lookup.end()) { return id; } + return iterator->second; + } + + /** \brief Returns the searched translation or returns the input, restricted to the context given by context. + * See https://www.gnu.org/software/gettext/manual/html_node/Contexts.html for more info. + * \param[in] context Restrict to the context given. + * \param[in] id The id of the translation to search for. + * \return The value you passed in via _id or the translated string. + */ + std::string LookupWithContext(const char *context, const char *id) const + { + std::string idName = context; + idName += '\x04'; + idName += id; + + if (m_lookup.empty()) return id; + auto iterator = m_lookup.find(idName); + + if (iterator == m_lookup.end()) { return id; } + return iterator->second; + } + + /// \brief Returns the Error Description. + const std::string &GetErrorDescription() const + { + return m_error; + } + + /// \brief Empties the Lookup-Table. + void ClearTable() + { + m_lookup.clear(); + } + + /** \brief Returns the Number of Entries in our Lookup-Table. + * \note The mo-File-table always contains an empty msgid, which contains informations + * about the tranlsation-project. So the real number of strings is always minus 1. + */ + unsigned int GetNumStrings() const + { + return m_lookup.size(); + } + + /** \brief Exports the whole content of the .mo-File as .html + * \param[in] infile The .mo-File to export. + * \param[in] filename Where to store the .html-file. If empty, the path and filename of the _infile with .html appended. + * \param[in,out] css The css-script for the visual style of the + * file, in case you don't like mine ;). + * \see g_css for the possible and used css-values. + */ + static eErrorCode ExportAsHTML(const std::string &infile, const std::string &filename = "", const std::string &css = g_css) + { + // Read the file + moFileReader reader; + moFileReader::eErrorCode r = reader.ReadFile(infile.c_str()); + if (r != moFileReader::EC_SUCCESS) { return r; } + if (reader.m_lookup.empty()) { return moFileReader::EC_TABLEEMPTY; } + + // Beautify Output + std::string fname; + unsigned int pos = infile.find_last_of(moPATHSEP); + if (pos != std::string::npos) { fname = infile.substr(pos + 1, infile.length()); } + else + { + fname = infile; + } + + // if there is no filename given, we set it to the .mo + html, e.g. test.mo.html + std::string htmlfile(filename); + if (htmlfile.empty()) { htmlfile = infile + std::string(".html"); } + + // Ok, now prepare output. + std::ofstream stream(htmlfile.c_str()); + if (stream.is_open()) + { + stream << R"()" + << std::endl; + stream << "" << std::endl; + stream << R"()" << std::endl; + stream << "Dump of " << fname << "" << std::endl; + stream << "" << std::endl; + stream << "
" << std::endl; + stream << "

" << fname << "

" << std::endl; + stream << R"()" << std::endl; + + std::stringstream parsee; + parsee << reader.Lookup(""); + + while (!parsee.eof()) + { + char buffer[1024]; + parsee.getline(buffer, 1024); + std::string name; + std::string value; + + reader.GetPoEditorString(buffer, name, value); + if (!(name.empty() || value.empty())) + { + stream << "" << std::endl; + } + } + stream << "
Project Info
" << name << "" << value << "
" << std::endl; + stream << "
" << std::endl; + + // Now output the content + stream << R"()" << std::endl; + for (const auto &it : reader.m_lookup) + { + if (!it.first.empty()) // Skip the empty msgid, its the table we handled above. + { + stream << "" << std::endl; + } + } + stream << "
Content
" << it.first << "" << it.second << "

" << std::endl; + + stream << "
" << std::endl; + stream << "
File generated by moFileReaderSDK
" + << std::endl; + stream << "" << std::endl; + stream.close(); + } + else + { + return moFileReader::EC_FILENOTFOUND; + } + + return moFileReader::EC_SUCCESS; + } + + protected: + /// \brief Keeps the last error as String. + std::string m_error; + + /** \brief Swap the endianness of a 4 byte WORD. + * \param[in] in The value to swap. + * \return The swapped value. + */ + unsigned long SwapBytes(unsigned long in) + { + unsigned long b0 = (in >> 0) & 0xff; + unsigned long b1 = (in >> 8) & 0xff; + unsigned long b2 = (in >> 16) & 0xff; + unsigned long b3 = (in >> 24) & 0xff; + + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + + private: + // Holds the lookup-table + moLookupList m_lookup; + + // Replaces < with ( to satisfy html-rules. + static void MakeHtmlConform(std::string &_inout) + { + std::string temp = _inout; + for (unsigned int i = 0; i < temp.length(); i++) + { + if (temp[i] == '>') { _inout.replace(i, 1, ")"); } + if (temp[i] == '<') { _inout.replace(i, 1, "("); } + } + } + + // Extracts a value-pair from the po-edit-information + bool GetPoEditorString(const char *_buffer, std::string &_name, std::string &_value) + { + std::string line(_buffer); + size_t first = line.find_first_of(':'); + + if (first != std::string::npos) + { + _name = line.substr(0, first); + _value = line.substr(first + 1, line.length()); + + // Replace <> with () for Html-Conformity. + MakeHtmlConform(_value); + MakeHtmlConform(_name); + + // Remove spaces from front and end. + Trim(_value); + Trim(_name); + + return true; + } + return false; + } + + // Removes spaces from front and end. + static void Trim(std::string &_in) + { + while (_in[0] == ' ') + { + _in = _in.substr(1, _in.length()); + } + while (_in[_in.length()] == ' ') + { + _in = _in.substr(0, _in.length() - 1); + } + } +}; + +/** \brief Convience Class + * + * + * This class derives from moFileReader and builds a singleton to access its methods + * in a global manner. + * \note This class is a Singleton. Please access it via moFileReaderSingleton::GetInstance() + * or use the provided wrappers:\n + * - moReadMoFile() + * - _() + * - moFileClearTable() + * - moFileGetErrorDescription() + * - moFileGetNumStrings(); + */ +class moFileReaderSingleton : public moFileReader +{ + private: + // Private Contructor and Copy-Constructor to avoid + // that this class is instanced. + moFileReaderSingleton() + { + } + + moFileReaderSingleton(const moFileReaderSingleton &); + + moFileReaderSingleton &operator=(const moFileReaderSingleton &) + { + return *this; + } + + public: + /** \brief Singleton-Accessor. + * \return A static instance of moFileReaderSingleton. + */ + static moFileReaderSingleton &GetInstance() + { + static moFileReaderSingleton theoneandonly; + return theoneandonly; + } +}; + +/** \brief Reads the .mo-File. + * \param[in] _filename The path to the file to use. + * \see moFileReader::ReadFile() for details. + */ +inline moFileReader::eErrorCode moReadMoFile(const char *_filename) +{ + moFileReader::eErrorCode r = moFileReaderSingleton::GetInstance().ReadFile(_filename); + return r; +} + +/** \brief Looks for the spec. string to translate. + * \param[in] id The string-id to search. + * \return The translation if found, otherwise it returns id. + */ +inline std::string _(const char *id) +{ + std::string r = moFileReaderSingleton::GetInstance().Lookup(id); + return r; +} + +/// \brief Resets the Lookup-Table. +inline void moFileClearTable() +{ + moFileReaderSingleton::GetInstance().ClearTable(); +} + +/// \brief Returns the last known error as string or an empty class. +inline std::string moFileGetErrorDescription() +{ + std::string r = moFileReaderSingleton::GetInstance().GetErrorDescription(); + return r; +} + +/// \brief Returns the number of entries loaded from the .mo-File. +inline int moFileGetNumStrings() +{ + int r = moFileReaderSingleton::GetInstance().GetNumStrings(); + return r; +} + +#if defined(_MSC_VER) + #pragma warning(default : 4251) +#endif /* _MSC_VER */ + +MO_END_NAMESPACE + +#endif /* __MOFILEREADER_SINGLE_INCLUDE_H_INCLUDED__ */ \ No newline at end of file diff --git a/src/mo.cpp b/src/mo.cpp index 09505dc..64871d9 100644 --- a/src/mo.cpp +++ b/src/mo.cpp @@ -1,7 +1,7 @@ /* * moFileReader - A simple .mo-File-Reader * Copyright (C) 2009 Domenico Gentner (scorcher24@gmail.com) - * Copyright (C) 2018 Edgar (Edgar@AnotherFoxGuy.com) + * Copyright (C) 2018-2021 Edgar (Edgar@AnotherFoxGuy.com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,140 +31,137 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "moFileReader.h" -#include +#include "moFileReader.hpp" + #include +#include #if defined(_MSC_VER) && defined(_DEBUG) -# include + #include #endif /* _MSC_VER */ using namespace moFileLib; -void Usage(const std::string appname) +void Usage(const std::string& appname) { - std::cout << "Usage: " << std::endl; - std::cout << appname << "