/* * moFileReader - A simple .mo-File-Reader * Copyright (C) 2009 Domenico Gentner (scorcher24@gmail.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 with Visual C++ (Express and better)

* * We provide a project for Visual C++ 2008. You can select 3 Types of Compilation: * *

Executable (Release or Debug)

* * This will compile the code as an executable which can lookup strings from every .mo-File you load with it. * This can be handy if you want to have a peek into a file or test something etc. I recommend building the * release-executable only if you just want to use it. * *

Dynamic Loaded Library ( ReleaseDLL )

* * This may be overkill, but perhaps you like it modular. This Configuration will create a dll and an import-library. * Do not forget to link against the import-library and please define MOFILE_IMPORT in your preprocessor-settings, * otherwise you will receive a bunch of linker-errors. * You will find all files in the directory "lib" in the Solutions Directory. * *

Static Library ( ReleaseLIB )

* * This will compile the code as a static library with no Entry-Point. This is the recommended usage. * But please do not forget to link against moFileReader.static.lib. Otherwise you will receive linker-errors. * *

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

Compilation via provided batch-files (woe32 only)

* * - Call compile_vc.bat or compile_mingw.bat with one of these options: * - DLL - Compiles as dynamic loaded module. * - LIB - Compiles as static library. * - EXE - Compiles the executable. * *

None of those?

* * 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. * If you can provide such a file with reversed bytes, please compile %%projectdir%%/bin/i18n/de/moTest.po with * gettext or poEdit and send it to scorcher24 [at] gmail [dot] com. * *

Changelog

* * - 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 * If you (dis)like it or have suggestions, questions, patches etc, please don't hesitate to write to my email-adress: scorcher24 [at] gmail [dot] com. * Submit patches and/or bugs on http://mofilereader.googlecode.com. You must register with googlemail to sign in. * 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,out] 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 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__ */