khanat-opennel-code/code/nel/include/nel/misc/config_file.h

355 lines
11 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NL_CONFIG_FILE_H
#define NL_CONFIG_FILE_H
#include "types_nl.h"
#include "common.h"
#include "debug.h"
#include "log.h"
#include <vector>
#include <string>
#include <cstdio>
namespace NLMISC
{
/**
* CConfigFile class. Useful when you want to have a configuration file with variables.
* It manages integers, real (double), and string basic types. A variable can be an array of
* basic type. In this case, all elements of the array must have the same type.
*
* If you setup the global callback before loading, it'll be called after the load() function.
*
* Example:
*\code
* try
* {
* CConfigFile cf;
*
* // Load and parse "test.txt" file
* cf.load ("test.txt");
*
* // Attach a callback to the var1 variable. When the var1 will change, this cvar1cb function will be called
* cf.setCallback ("var1", var1cb);
*
* // Get the foo variable (suppose it's a string variable)
* CConfigFile::CVar &foo = cf.getVar ("foo");
*
* // Display the content of the variable
* printf ("foo = %s\n", foo.asString ().c_str ());
*
* // Get the bar variable (suppose it's an array of int)
* CConfigFile::CVar &bar = cf.getVar ("bar");
*
* // Display the content of all the elements of the bar variable
* printf ("bar have %d elements : \n", bar.size ());
* for (int i = 0; i < bar.size (); i++)
* printf ("%d ", bar.asInt (i));
* printf("\n");
* }
* catch (const EConfigFile &e)
* {
* // Something goes wrong... catch that
* printf ("%s\n", e.what ());
* }
*\endcode
*
* Example of config file:
*\code
* // one line comment
* / * big comment
* on more than one line * /
*
* var1 = 123; // var1 type:int, value:123
* var2 = "456.25"; // var2 type:string, value:"456.25"
* var3 = 123.123; // var3 type:real, value:123.123
*
* // the resulting type is type of the first left value
* var4 = 123.123 + 2; // var4 type:real, value:125.123
* var5 = 123 + 2.1; // var5 type:int, value:125
*
* var6 = (-112+1) * 3 - 14; // var6 type:int, value:-347
*
* var7 = var1 + 1; // var7 type:int, value:124
*
* var8 = var2 + 10; // var8 type:string, value:456.2510 (convert 10 into a string and concat it)
* var9 = 10.15 + var2; // var9 type:real, value:466.4 (convert var2 into a real and add it)
*
* var10 = { 10.0, 51.1 }; // var10 type:realarray, value:{10.0,51.1}
* var11 = { "str1", "str2", "str3" }; // var11 type:stringarray, value:{"str1", "str2", "str3"}
*
* var12 = { 10+var1, var1-var7 }; // var12 type:intarray, value:{133,-1}
*\endcode
*
* Operators are '+', '-', '*', '/'.
* You can't use operators on a array variable, for example, you can't do \cvar13=var12+1.
* If you have 2 variables with the same name, the first value will be remplaced by the second one.
*
* \bug if you terminate the config file with a comment without carriage returns it'll generate an exception, add a carriage returns
*
* \author Vianney Lecroart
* \author Nevrax France
* \date 2000
*/
class CConfigFile
{
public:
/**
* CVar class. Used by CConfigFile. A CVar is returned when you want to have a variable.
*
* Example: see the CConfigFile example
*
* \author Vianney Lecroart
* \author Nevrax France
* \date 2000
*/
struct CVar
{
public:
CVar () : Type(T_UNKNOWN), Root(false), Comp(false), FromLocalFile(true), SaveWrap(6) {}
/// \name Access to the variable content.
//@{
/// Get the content of the variable as an integer
int asInt (int index=0) const;
/// Get the content of the variable as a double
double asDouble (int index=0) const;
/// Get the content of the variable as a float
float asFloat (int index=0) const;
/// Get the content of the variable as a STL string
std::string asString (int index=0) const;
/// Get the content of the variable as a boolean
bool asBool (int index=0) const;
//@}
/// \name Set the variable content.
/// If the index is the size of the array, the value will be append at the end.
//@{
/// Set the content of the variable as an integer
void setAsInt (int val, int index=0);
/// Set the content of the variable as a double
void setAsDouble (double val, int index=0);
/// Set the content of the variable as a float
void setAsFloat (float val, int index=0);
/// Set the content of the variable as a STL string
void setAsString (const std::string &val, int index=0);
/// Force the content of the variable to be a single integer
void forceAsInt (int val);
/// Force the content of the variable to be a single double
void forceAsDouble (double val);
/// Force the content of the variable to be a single string
void forceAsString (const std::string &val);
/// Set the content of the aray variable as an integer
void setAsInt (const std::vector<int> &vals);
/// Set the content of the aray variable as a double
void setAsDouble (const std::vector<double> &vals);
/// Set the content of the aray variable as a float
void setAsFloat (const std::vector<float> &vals);
/// Set the content of the aray variable as a string
void setAsString (const std::vector<std::string> &vals);
//@}
bool operator== (const CVar& var) const;
bool operator!= (const CVar& var) const;
// add this variable with var
void add (const CVar &var);
// Get the size of the variable. It's the number of element of the array or 1 if it's not an array.
uint size () const;
/// \name Internal use
//@{
static const char *TypeName[];
enum TVarType { T_UNKNOWN, T_INT, T_STRING, T_REAL, T_BOOL };
std::string Name;
TVarType Type;
bool Root; // true if this var comes from the root document. false else.
bool Comp; // true if the parser found a 'complex' var (ie an array)
bool FromLocalFile; // Used during cfg parsing. True if the var has been created from the currently parsed cfg
std::vector<int> IntValues;
std::vector<double> RealValues;
std::vector<std::string> StrValues;
int SaveWrap;
void (*Callback)(CVar &var);
//@}
};
CConfigFile() : _Callback(NULL) {}
virtual ~CConfigFile ();
/// Get a variable with the variable name
CVar &getVar (const std::string &varName);
/// Get a variable pointer with the variable name, without throwing exception. Return NULL if not found.
CVar *getVarPtr (const std::string &varName);
/// Get the variable count.
uint getNumVar () const;
/// Get a variable.
CVar *getVar (uint varId);
/// Add a variable. If the variable already exists, return it.
CVar *insertVar (const std::string &varName, const CVar &varToCopy);
/// Return true if the variable exists, false otherwise
bool exists (const std::string &varName);
/// load and parse the file
void load (const std::string &fileName, bool lookupPaths = false);
/// save the config file
void save () const;
/// Clear all the variable array (including information on variable callback etc)
void clear ();
/// set to 0 or "" all variable in the array (but not destroy them)
void clearVars ();
/// Returns true if the file has been loaded
bool loaded();
/// Returns the number of variables in the configuration
uint32 getVarCount();
/// reload and reparse the file
void reparse (bool lookupPaths = false);
/// display all variables with nlinfo (debug use)
void display () const;
/// display all variables with nlinfo (debug use)
void display (CLog *log) const;
/// set a callback function that is called when the config file is modified
void setCallback (void (*cb)());
/// set a callback function to a variable, it will be called when this variable is modified
void setCallback (const std::string &VarName, void (*cb)(CConfigFile::CVar &var));
/// contains the variable names that getVar() and getVarPtr() tried to access but not present in the cfg
std::vector<std::string> UnknownVariables;
/// returns the config file name
std::string getFilename () const { return FileNames[0]; }
/// set the time between 2 file checking (default value is 1 second)
/// \param timeout time in millisecond, if timeout=0, the check will be made each "frame"
static void setTimeout (uint32 timeout);
/// Internal use only
static void checkConfigFiles ();
private:
/// Internal use only
void (*_Callback)();
/// Internal use only
std::vector<CVar> _Vars;
// contains the configfilename (0) and roots configfilenames
// std::string _FileName;
// std::vector<uint32> _LastModified;
// contains the configfilename (0) and roots configfilenames
std::vector<std::string> FileNames;
std::vector<uint32> LastModified;
static uint32 _Timeout;
static std::vector<CConfigFile *> *_ConfigFiles;
};
struct EConfigFile : public Exception
{
EConfigFile() { _Reason = "Unknown Config File Exception";}
};
struct EBadType : public EConfigFile
{
EBadType (const std::string &varName, int varType, int wantedType)
{
static char str[NLMISC::MaxCStringSize];
smprintf (str, NLMISC::MaxCStringSize, "Bad variable type, variable \"%s\" is a %s and not a %s", varName.c_str (), CConfigFile::CVar::TypeName[varType], CConfigFile::CVar::TypeName[wantedType]);
_Reason = str;
nlinfo("CF: Exception will be launched: %s", _Reason.c_str());
}
};
struct EBadSize : public EConfigFile
{
EBadSize (const std::string &varName, int varSize, int varIndex)
{
static char str[NLMISC::MaxCStringSize];
smprintf (str, NLMISC::MaxCStringSize, "Trying to access to the index %d but the variable \"%s\" size is %d", varIndex, varName.c_str (), varSize);
_Reason = str;
nlinfo("CF: Exception will be launched: %s", _Reason.c_str());
}
};
struct EUnknownVar : public EConfigFile
{
EUnknownVar (const std::string &filename, const std::string &varName)
{
static char str[NLMISC::MaxCStringSize];
smprintf (str, NLMISC::MaxCStringSize, "variable \"%s\" not found in file \"%s\"", varName.c_str (), filename.c_str());
_Reason = str;
nlinfo("CF: Exception will be launched: %s", _Reason.c_str());
}
};
struct EParseError : public EConfigFile
{
EParseError (const std::string &fileName, int currentLine)
{
static char str[NLMISC::MaxCStringSize];
smprintf (str, NLMISC::MaxCStringSize, "Parse error on the \"%s\" file, line %d", fileName.c_str (), currentLine);
_Reason = str;
nlinfo("CF: Exception will be launched: %s", _Reason.c_str());
}
};
struct EFileNotFound : public EConfigFile
{
EFileNotFound (const std::string &fileName)
{
static char str[NLMISC::MaxCStringSize];
smprintf (str, NLMISC::MaxCStringSize, "File \"%s\" not found", fileName.c_str ());
_Reason = str;
}
};
} // NLMISC
#endif // NL_CONFIG_FILE_H
/* End of config_file.h */