🎨 Convert to a header-only library (#6)

This commit is contained in:
Edgar 2021-01-12 16:19:25 +01:00 committed by GitHub
parent f7fe31739e
commit c7b0151510
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1090 additions and 1404 deletions

24
.clang-format Normal file
View File

@ -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

21
.github/workflows/main.yml vendored Normal file
View File

@ -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 .

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -1,7 +0,0 @@
from conan.packager import ConanMultiPackager
if __name__ == "__main__":
builder = ConanMultiPackager()
builder.add_common_builds()
builder.run()

17
cmake/FindCatch2.cmake Normal file
View File

@ -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}")

View File

@ -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")

View File

@ -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__ */

View File

@ -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 <deque>
#include <map>
#include <fstream>
#include <cstring> // this is for memset when compiling with gcc.
#include <string>
#include <sstream>
#ifndef __MOFILECONFIG_H_INCLUDED__
# include "moFileConfig.h"
#endif
/** \mainpage moFileReaderSDK
*
* <h2>Compilation via cmake</h2>
*
* - 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
*
*
* <h2>Include in project</h2>
*
* 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.
*
* <h2>Usage</h2>
*
* 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.
*
* <h2>Changelog</h2>
*
* - 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
*
*
* <h2>Credits</h2>
*
* Gettext is part of the GNU-Tools and (C) by the <a href="http://fsf.org">Free Software Foundation</a>.\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<moTranslationPairInformation> 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<std::string, std::string> 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__ */

901
include/moFileReader.hpp Normal file
View File

@ -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 <cstring> // this is for memset when compiling with gcc.
#include <deque>
#include <fstream>
#include <map>
#include <sstream>
#include <string>
//-------------------------------------------------------------
// 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
*
*
* <h2>Include in project</h2>
*
* 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.
*
* <h2>Usage</h2>
*
* 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.
*
* <h2>Changelog</h2>
*
* - 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
*
*
* <h2>Credits</h2>
*
* Gettext is part of the GNU-Tools and (C) by the <a href="http://fsf.org">Free Software Foundation</a>.\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<moTranslationPairInformation> 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<std::string, std::string> 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"(<!DOCTYPE HTML PUBLIC "- //W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">)"
<< std::endl;
stream << "<html><head><style type=\"text/css\">\n" << std::endl;
stream << css << std::endl;
stream << "</style>" << std::endl;
stream << R"(<meta http-equiv="content-type" content="text/html; charset=utf-8">)" << std::endl;
stream << "<title>Dump of " << fname << "</title></head>" << std::endl;
stream << "<body>" << std::endl;
stream << "<center>" << std::endl;
stream << "<h1>" << fname << "</h1>" << std::endl;
stream << R"(<table border="1"><th colspan="2">Project Info</th>)" << 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 << "<tr><td>" << name << "</td><td>" << value << "</td></tr>" << std::endl;
}
}
stream << "</table>" << std::endl;
stream << "<hr noshade/>" << std::endl;
// Now output the content
stream << R"(<table border="1"><th colspan="2">Content</th>)" << std::endl;
for (const auto &it : reader.m_lookup)
{
if (!it.first.empty()) // Skip the empty msgid, its the table we handled above.
{
stream << "<tr><td>" << it.first << "</td><td>" << it.second << "</td></tr>" << std::endl;
}
}
stream << "</table><br/>" << std::endl;
stream << "</center>" << std::endl;
stream << "<div class=\"copyleft\">File generated by <a href=\"https://github.com/AnotherFoxGuy/MofileReader\" "
"target=\"_blank\">moFileReaderSDK</a></div>"
<< std::endl;
stream << "</body></html>" << 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__ */

View File

@ -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,9 +31,10 @@
* 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 <iostream>
#include "moFileReader.hpp"
#include <cstdlib>
#include <iostream>
#if defined(_MSC_VER) && defined(_DEBUG)
#include <crtdbg.h>
@ -41,7 +42,7 @@
using namespace moFileLib;
void Usage(const std::string appname)
void Usage(const std::string& appname)
{
std::cout << "Usage: " << std::endl;
std::cout << appname << " <option> <params>" << std::endl;
@ -59,37 +60,38 @@ void Usage(const std::string appname)
void PrintLicense()
{
std::cout << "\
moFileReader - A simple .mo-File-Reader\n\
Copyright (C) 2009 Domenico Gentner (scorcher24@gmail.com)\n\
All rights reserved. \n\
\n\
Redistribution and use in source and binary forms, with or without\n\
modification, are permitted provided that the following conditions\n\
are met:\n\
\n\
1. Redistributions of source code must retain the above copyright\n\
notice, this list of conditions and the following disclaimer.\n\
2. Redistributions in binary form must reproduce the above copyright\n\
notice, this list of conditions and the following disclaimer in the\n\
documentation and/or other materials provided with the distribution.\n\
\n\
3. The names of its contributors may not be used to endorse or promote \n\
products derived from this software without specific prior written \n\
permission.\n\
\n\
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\
\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n\
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n\
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n\
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n\
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n\
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n\
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n\
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\
" << std::endl;
std::cout << R"(
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.
)" << std::endl;
}
std::string GetAppName(const char *raw)
@ -100,8 +102,6 @@ std::string GetAppName(const char* raw)
return r;
}
#if defined(_CONSOLE)
int main(int, char **argv)
{
#if defined(_DEBUG) && defined(_MSC_VER)
@ -143,10 +143,7 @@ int main( int, char** argv )
Usage(appname);
return EXIT_FAILURE;
}
if ( argv[3] )
{
outfile = argv[3];
}
if (argv[3]) { outfile = argv[3]; }
moFileReader::eErrorCode r = moFileReader::ExportAsHTML(argv[2], outfile);
if (r == moFileReader::EC_SUCCESS)
@ -203,30 +200,3 @@ int main( int, char** argv )
return EXIT_FAILURE;
}
}
#elif defined(_USRDLL) && defined(WIN32)
#include <windows.h>
extern "C"
int WINAPI DllMain( DWORD reason, LPVOID)
{
switch (reason)
{
case DLL_THREAD_ATTACH:
case DLL_PROCESS_ATTACH:
{
#if defined (_DEBUG) && defined(_MSC_VER)
long flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flag |= _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF;
_CrtSetDbgFlag(flag);
#endif /* _MSC_VER && _DEBUG */
break;
}
default:
return FALSE;
};
return TRUE;
}
#endif /* Compilation-Mode */

View File

@ -1,526 +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.
*/
#include "moFileReader.h"
#include <iostream>
#include <cstdlib>
MO_BEGIN_NAMESPACE
unsigned long moFileReader::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;
}
const std::string& moFileReader::GetErrorDescription() const
{
return m_error;
}
void moFileReader::ClearTable()
{
m_lookup.clear();
}
unsigned int moFileReader::GetNumStrings() const
{
return m_lookup.size();
}
std::string moFileReader::Lookup( const char* id ) const
{
if ( m_lookup.size() <= 0) return id;
moLookupList::const_iterator iterator = m_lookup.find(id);
if ( iterator == m_lookup.end() )
{
return id;
}
return iterator->second;
}
std::string moFileReader::LookupWithContext (const char* context, const char* id) const
{
std::string idName = context;
idName += '\x04';
idName += id;
if (m_lookup.size () <= 0) return id;
moLookupList::const_iterator iterator = m_lookup.find (idName);
if (iterator == m_lookup.end ())
{
return id;
}
return iterator->second;
}
moFileReader::eErrorCode moFileReader::ParseData(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;
}
moFileReader::eErrorCode moFileReader::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;
}
moFileReader::eErrorCode moFileReader::ExportAsHTML(std::string infile, std::string filename, std::string 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 << "<!DOCTYPE HTML PUBLIC \"- //W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" << std::endl;
stream << "<html><head><style type=\"text/css\">\n" << std::endl;
stream << css << std::endl;
stream << "</style>" << std::endl;
stream << "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">" << std::endl;
stream << "<title>Dump of " << fname << "</title></head>" << std::endl;
stream << "<body>" << std::endl;
stream << "<center>" <<std::endl;
stream << "<h1>" << fname << "</h1>" << std::endl;
stream << "<table border=\"1\"><th colspan=\"2\">Project Info</th>" << 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 << "<tr><td>" << name << "</td><td>" << value << "</td></tr>" << std::endl;
}
}
stream << "</table>" << std::endl;
stream << "<hr noshade/>" << std::endl;
// Now output the content
stream << "<table border=\"1\"><th colspan=\"2\">Content</th>" << std::endl;
for ( moLookupList::const_iterator it = reader.m_lookup.begin();
it != reader.m_lookup.end(); it++)
{
if ( it->first != "" ) // Skip the empty msgid, its the table we handled above.
{
stream << "<tr><td>" << it->first << "</td><td>" << it->second << "</td></tr>" << std::endl;
}
}
stream << "</table><br/>" << std::endl;
stream << "</center>" << std::endl;
stream << "<div class=\"copyleft\">File generated by <a href=\"https://github.com/AnotherFoxGuy/MofileReader\" target=\"_blank\">moFileReaderSDK</a></div>" << std::endl;
stream << "</body></html>" << std::endl;
stream.close();
}
else
{
return moFileReader::EC_FILENOTFOUND;
}
return moFileReader::EC_SUCCESS;
}
// Removes spaces from front and end.
void moFileReader::Trim(std::string& in)
{
while ( in[0] == ' ' )
{
in = in.substr(1, in.length() );
}
while( in[in.length()] == ' ' )
{
in = in.substr(0, in.length() - 1 );
}
}
// Extracts a value-pair from the po-edit-information
bool moFileReader::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;
}
// Replaces < with ( to satisfy html-rules.
void moFileReader::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, "(");
}
}
}
moFileReaderSingleton& moFileReaderSingleton::GetInstance()
{
static moFileReaderSingleton theoneandonly;
return theoneandonly;
}
moFileReaderSingleton::moFileReaderSingleton(const moFileReaderSingleton& )
{
}
moFileReaderSingleton::moFileReaderSingleton()
{
}
moFileReaderSingleton& moFileReaderSingleton::operator=(const moFileReaderSingleton&)
{
return *this;
}
MO_END_NAMESPACE

View File

@ -1,17 +1,16 @@
if(NOT EXISTS "${CMAKE_BINARY_DIR}/catch/catch.hpp")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/catch")
message(STATUS "Downloading catch.hpp from https://github.com/catchorg/Catch2/")
file(DOWNLOAD "https://github.com/catchorg/Catch2/releases/download/v2.5.0/catch.hpp"
"${CMAKE_BINARY_DIR}/catch/catch.hpp")
endif()
project(moFileReaderTest)
find_package(Catch2 REQUIRED)
add_executable(moFileReaderTest test.cpp)
target_include_directories(moFileReaderTest PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/catch)
target_link_libraries(moFileReaderTest moFileReader)
add_test(NAME mo_test COMMAND moFileReaderTest)
include(CTest)
include(Catch)
add_executable(${PROJECT_NAME} test.cpp link_test.cpp)
target_include_directories(moFileReaderTest PRIVATE ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(${PROJECT_NAME} PRIVATE Catch2::Catch2)
catch_discover_tests(${PROJECT_NAME})
add_custom_command(
TARGET moFileReaderTest POST_BUILD
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/test.mo $<TARGET_FILE_DIR:moFileReaderTest>/test.mo
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/test.mo ${CMAKE_CURRENT_BINARY_DIR}/test.mo
)

17
test/link_test.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "catch.hpp"
#include "moFileReader.hpp"
using namespace moFileLib;
#define _L(str) moFileReaderSingleton::GetInstance().Lookup(str)
#define _LC(ctx, str) moFileReaderSingleton::GetInstance().LookupWithContext(ctx, str)
void test()
{
moFileReaderSingleton::GetInstance().ReadFile("test.mo");
/* This is the first comment. */
CHECK("Text Nederlands Een" == _L("String English One"));
/* This is the second comment. */
CHECK("Text Nederlands Twee" == _L("String English Two"));
/* This is the third comment. */
CHECK("Text Nederlands Drie" == _L("String English Three"));
}

View File

@ -1,7 +1,7 @@
#define CATCH_CONFIG_MAIN
#include "moFileReader.h"
#include "catch.hpp"
#include "moFileReader.hpp"
using namespace moFileLib;
#define _L(str) moFileReaderSingleton::GetInstance().Lookup(str)
@ -23,7 +23,6 @@ TEST_CASE ("Lookup string", "[Lookup]")
CHECK("Text Nederlands Drie" == _L("String English Three"));
}
TEST_CASE("Lookup string with context", "[LookupWithContext]")
{
moFileReaderSingleton::GetInstance().ReadFile("test.mo");

View File

@ -1,31 +0,0 @@
<!DOCTYPE HTML PUBLIC "- //W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><style type="text/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;}
</style>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Dump of test.mo</title></head>
<body>
<center>
<h1>test.mo</h1>
<table border="1"><th colspan="2">Project Info</th>
<tr><td>Project-Id-Version</td><td>moFileTest</td></tr>
<tr><td>POT-Creation-Date</td><td>2012-05-20 00:51+0100</td></tr>
<tr><td>PO-Revision-Date</td><td>2012-05-20 00:57+0100</td></tr>
<tr><td>Last-Translator</td><td>scorcher24 &lt;scorcher24@gmail.&gt;om></td></tr>
<tr><td>MIME-Version</td><td>1.0</td></tr>
<tr><td>Content-Type</td><td>text/plain; charset=UTF-8</td></tr>
<tr><td>Content-Transfer-Encoding</td><td>8bit</td></tr>
<tr><td>X-Poedit-KeywordsList</td><td>_;gettext;gettext_noop</td></tr>
<tr><td>X-Poedit-Basepath</td><td>.</td></tr>
<tr><td>X-Poedit-SearchPath-0</td><td>.</td></tr>
</table>
<hr noshade/>
<table border="1"><th colspan="2">Content</th>
<tr><td>String English One</td><td>Zeichenkette Englisch Eins</td></tr>
<tr><td>String English Three</td><td>Zeichenkette Englisch Drei</td></tr>
<tr><td>String English Two</td><td>Zeichenkette Englisch Zwei </td></tr>
</table><br/>
</center>
<div class="copyleft">File generated by <a href="https://github.com/AnotherFoxGuy/MofileReader" target="_blank">moFileReaderSDK</a></div>
</body></html>