/****************************************************************************** * Copyright (C) 2012 Matthew Goff * * * * This software is provided 'as-is', without any express or implied * * warranty. In no event will the authors be held liable for any damages * * arising from the use of this software. * * * * Permission is granted to anyone to use this software for any purpose, * * including commercial applications, and to alter it and redistribute it * * freely, subject to the following restrictions: * * * * 1. The origin of this software must not be misrepresented; you must not * * claim that you wrote the original software. If you use this software * * in a product, an acknowledgment in the product documentation would be * * appreciated but is not required. * * * * 2. Altered source versions must be plainly marked as such, and must not * * be misrepresented as being the original software. * * * * 3. This notice may not be removed or altered from any source distribution. * * * * Matthew Goff (matt@goff.cc) <http://www.ackmud.net/> * ******************************************************************************/ /** * @file utils.h * @brief The Utils namespace. * * This file contains the Utils namespace and template functions. */ #ifndef DEC_UTILS_H #define DEC_UTILS_H #include <algorithm> #include <bitset> #include <cstdarg> #include <cstdio> #include <fstream> #include <iostream> #include <iterator> #include <map> #include <sstream> #include <vector> #include <dirent.h> #include <errno.h> #include <string.h> #include <sys/stat.h> #include <sys/time.h> using namespace std; enum UTILS_OPTS { UTILS_DEBUG = 0, /**< Enables debugging output in Utils::_Logger(). This typically includes the calling function's file and line number. */ UTILS_RAW = 1, /**< Raw data output. Skips prepending / appending anything on Utils::_Logger writes (no timestamp, etc). */ UTILS_TYPE_ERROR = 2, /**< Indicates an error and prepends #CFG_STR_UTILS_ERROR to Utils::_Logger() output. */ UTILS_TYPE_INFO = 3, /**< Indicates an info message and prepends #CFG_STR_UTILS_INFO to Utils::_Logger() output. */ UTILS_TYPE_SOCKET = 4, /**< Indicates a socket related message and prepends #CFG_STR_UTILS_SOCKET to Utils::_Logger() output. */ UTILS_TIME_S = 5, /**< Will return time as @a seconds from Utils::DiffTime(). */ UTILS_TIME_MS = 6, /**< Will return time as @a milliseconds from Utils::DiffTime(). */ UTILS_TIME_US = 7, /**< Will return time as @a microseconds from Utils::DiffTime(). */ MAX_UTILS = 8 /**< Safety limit for looping. */ }; #define UTILS_IS_DIRECTORY true #define UTILS_IS_FILE false /** * @def CFG_MEM_MAX_BITSET * @brief Maximum size of all bitset elements within the server. * @par Default: 32 */ #define CFG_MEM_MAX_BITSET 32 /** * @def CFG_STR_UTILS_ERROR * @brief String to prepend to logs flagged UTILS_TYPE_ERROR. * @par Default: "[ERROR ] " */ #define CFG_STR_UTILS_ERROR "[ERROR] " /** * @def CFG_STR_UTILS_INFO * @brief String to prepend to logs flagged UTILS_TYPE_INFO. * @par Default: "[INFO ] " */ #define CFG_STR_UTILS_INFO "[INFO ] " /** * @def CFG_STR_UTILS_SOCKET * @brief String to prepend to logs flagged UTILS_TYPE_SOCKET. * @par Default: "[SOCKET] " */ #define CFG_STR_UTILS_SOCKET "[SOCKET] " /** * @def PP_NARG * @author Laurent Deniau @ http://goo.gl/3jkAm * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list. * @param[in] ... A variable length argument list to determine the count of. */ #define PP_NARG(...) \ PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) /** * @def PP_NARG_ * @author Laurent Deniau @ http://goo.gl/3jkAm * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list. */ #define PP_NARG_(...) \ PP_ARG_N(__VA_ARGS__) /** * @def PP_ARG_N * @author Laurent Deniau @ http://goo.gl/3jkAm * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list. */ #define PP_ARG_N( \ _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ _61,_62,_63,N,...) N /** * @def PP_RSEQ_N * @author Laurent Deniau @ http://goo.gl/3jkAm * @brief Determine of the number of arguments passed to a function from a __VA_ARGS__ list. */ #define PP_RSEQ_N() \ 63,62,61,60, \ 59,58,57,56,55,54,53,52,51,50, \ 49,48,47,46,45,44,43,42,41,40, \ 39,38,37,36,35,34,33,32,31,30, \ 29,28,27,26,25,24,23,22,21,20, \ 19,18,17,16,15,14,13,12,11,10, \ 9,8,7,6,5,4,3,2,1,0 /** * @def STR * @brief Stringify the calling function's file and line number for debugging. * @param[in] x An item to be stringified. */ #define STR(x) #x /** * @def SX * @brief Stringify the calling function's file and line number for debugging. * @parampin] x An item to be stringified. */ #define SX(x) STR(x) /** * @def _caller_ * @brief Stringify the calling function's file and line number for debugging. */ #define _caller_ __FILE__ ":" SX(__LINE__) /** * @def BSET * @brief Define a bitset variable (name) with values (value) already set. * @param[in] name The name to use for declaring a local variable of bitset<#CFG_MEM_MAX_BITSET>. * @param[in] value Values from #UTILS_OPTS to be enabled on name. */ #define BSET( name, value ) \ bitset<CFG_MEM_MAX_BITSET> name; \ name.set( value ) /** * @def CSTR * @brief Output std::string to const char*. * @param[in] str A std::string object. */ #define CSTR( str ) ( str ).c_str() /** * @def LOGSTR * @brief Wrap Utils::Logger() for brevity and ease of future maintenance. * @param[in] flags A local variable name of type bitset<#CFG_MEM_MAX_BITSET> with #UTILS_OPTS enabled as appropriate. 0 may be used if no options are needed. * @param[in] message Any string, char, const char, numeric value, etc. This message will be written to log. */ #define LOGSTR( flags, message ) Utils::Logger( flags, message ) /** * @def LOGFMT * @brief Wrap Utils::FormatString() within Utils::Logger() for brevity and ease of future maintenance. * @param[in] flags A local variable name of type bitset<#CFG_MEM_MAX_BITSET> with #UTILS_OPTS enabled as appropriate. 0 may be used if no options are needed. * @param[in] message Any string that contains printf style format variables. * @param[in] ... The list of arguments to format into message. */ #define LOGFMT( flags, message, ... ) LOGSTR( flags, Utils::FormatString( flags, message, __VA_ARGS__ ) ) /** * @def LOGERRNO * @brief Wrap Utils::Logger() based on a locally generated errno value from system functions. * @param[in] flags A local variable name of type bitset<#CFG_MEM_MAX_BITSET> with #UTILS_OPTS enabled as appropriate. 0 may be used if no options are needed. * @param[in] message Any string that contains printf style format variables. */ #define LOGERRNO( flags, message ) LOGFMT( flags, message " returned errno %d: %s", errno, strerror( errno ) ) /** * @def UFLAGS_DE * @brief Define a bitset variable (name) with #UTILS_DEBUG and #UTILS_TYPE_ERROR already enabled. * @param[in] name The name to use for declaring a local variable of bitset<#CFG_MEM_MAX_BITSET>. */ #define UFLAGS_DE( name ) BSET( name, UTILS_DEBUG & UTILS_TYPE_ERROR ) /** * @brief The Utils namespace contains all general purpose, multi-use, and non-class functions. */ namespace Utils { /** * @brief Returns a string with all whitespace characters removed. * @param[in] t Any type of string to remove whitespace from. * @retval string A string with all whitespace characters removed. */ template <class T> inline const string DelSpaces( const T& t ) { string output( t ); output.erase( remove_if( output.begin(), output.end(), ::isspace ), output.end() ); return output; } /** * @brief Returns a string converted to all lowercase letters. * @param[in] t Any type of string to convert to lowercase. * @retval string A string converted to all lowercase letters. */ template <class T> inline const string Lower( const T& t ) { string output( t ); transform( output.begin(), output.end(), output.begin(), ::tolower ); return output; } /** * @brief Returns a string converted to all uppercase letters. * @param[in] t Any type of string to convert to uppercase. * @retval string A string converted to all uppercase letters. */ template <class T> inline const string Upper( const T& t ) { string output( t ); transform( output.begin(), output.end(), output.begin(), ::toupper ); return output; } /** * @brief Returns a string object from any data type: bool, int, char, etc. * @param[in] t Any type of data that can be represented as an alphanumeric string. * @retval string A string containing the converted data. */ template <class T> inline const string String( const T& t ) { stringstream ss( t ); return ss.str(); } const timeval CurrentTime(); const long unsigned int DiffTime( const timeval& prev, const timeval& current, const long unsigned int& granularity ); const string DirPath( const string& directory, const string& file, const string& ext = "" ); const string _FormatString( const long unsigned int& narg, const bitset<CFG_MEM_MAX_BITSET>& flags, const string& caller, const string& fmt, ... ); const string __FormatString( const long unsigned int& narg, const bitset<CFG_MEM_MAX_BITSET>& flags, const string& caller, const string& fmt, va_list& val ); #define FormatString( flags, fmt, ... ) _FormatString( PP_NARG( __VA_ARGS__ ), flags, _caller_, fmt, ##__VA_ARGS__ ) const void _Logger( const long unsigned int& narg, const bitset<CFG_MEM_MAX_BITSET>& flags, const string& caller, const string& fmt, ... ); #define Logger( flags, fmt, ... ) _Logger( PP_NARG( __VA_ARGS__ ), flags, _caller_, fmt, ##__VA_ARGS__ ) const long unsigned int NumChar( const string& input, const string& item ); const vector<string> StrNewlines( const string& input ); const string StrTime( const timeval& now ); const vector<string> StrTokens( const string& input ); const bool iDirectory( const string& dir ); const bool iFile( const string& file ); const bool iNumber( const string& input ); const bool iReadable( const string& file ); /** * @brief The DeleteObject class implements only operator() to be used in easily deleteing lists of pointers or other objects. */ class DeleteObject { public: /** * @brief Deletes a pointer. * @param[in] ptr The pointer to be deleted. * @retval void */ template <class T> inline const void operator() ( const T* ptr ) const { delete ptr; } }; /** * @brief Splits a string in the format of key=value. Retains any whitespace in the value. * @param[in] key The object to be populated with the key extracted from item. * @param[in] val The object to be populated with the value extracted from item. * @param[in] item A char or string in the form of key=value to extract data from. * @retval false Returned if item does not contain an equal sign. * @retval true Returned if data was successfully extracted from item. */ template <class K, class V, class I> inline const bool KeyValue( K& key, V& val, const I& item ) { size_t loc = 0; if ( ( loc = item.find( "=" ) ) == string::npos ) return false; key = item.substr( 0, loc - 1 ); val = item.substr( loc + 1, item.length() ); loc = key.find_last_not_of( " " ); key.erase( loc + 1 ); loc = val.find_first_not_of( " " ); val.erase( 0, loc ); return true; } /** * @brief If the contents of keyd == valu, assigns loc = item. * @param[in] igncase If true, performs case-insensitive matching. * @param[in] found For loop control. If keyd == valu, set to true, otherwise false. * @param[in] keyd A string or number that valu must match. * @param[in] valu A string or number that is checked for a match against keyd. * @param[in] item A string to be copied to to loc if keyd == valu. * @param[in] loc A string to be assigned the value of item if keyd == valu. * @retval void */ template <class K, class V> inline const void KeySet( const bool& igncase, bool& found, const K& keyd, const V& valu, const string& item, string& loc ) { string key( keyd ); string val( valu ); if ( igncase ) { transform( key.begin(), key.end(), key.begin(), ::tolower ); transform( val.begin(), val.end(), val.begin(), ::tolower ); } if ( key.compare( val ) == 0 ) { loc = item; found = true; return; } return; } /** * @brief If the contents of keyd == valu, assigns loc = item. * @param[in] igncase If true, performs case-insensitive matching. * @param[in] found For loop control. If keyd == valu, set to true, otherwise false. * @param[in] keyd A string or number that valu must match. * @param[in] valu A string or number that is checked for a match against keyd. * @param[in] item Any string, digit, etc to be copied to to loc if keyd == valu. * @param[in] loc Any string, digit, etc to be assigned the value of item if keyd == valu. * @retval void */ template <class K, class V, class I, class L> inline const void KeySet( const bool& igncase, bool& found, const K& keyd, const V& valu, const I& item, L& loc ) { string key( keyd ); string val( valu ); if ( igncase ) { transform( key.begin(), key.end(), key.begin(), ::tolower ); transform( val.begin(), val.end(), val.begin(), ::tolower ); } if ( key.compare( val ) == 0 ) { string tf( item ); // Allow bools be any of: true / 1 or false / 0 transform( tf.begin(), tf.end(), tf.begin(), ::tolower ); if ( tf.compare( "true" ) == 0 ) loc = true; else if ( tf.compare( "false" ) == 0 ) loc = false; else stringstream( item ) >> loc; found = true; return; } return; } const multimap<bool,string> ListDirectory( const string& dir, const bool& recursive, multimap<bool,string>& output, long unsigned int& dir_close, long unsigned int& dir_open ); }; #endif