# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

config_file.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2000 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "../stdmisc.h"
00027 
00028 #include <time.h>
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031 
00032 #include "nel/misc/file.h"
00033 #include "nel/misc/debug.h"
00034 #include "nel/misc/config_file.h"
00035 #include "nel/misc/path.h"
00036 
00037 using namespace std;
00038 using namespace NLMISC;
00039 
00040 extern void cfrestart (FILE *); // used to reinit the file
00041 extern int cfparse (void *);    // used to parse the file
00042 //extern FILE *cfin;
00043 extern int cf_CurrentLine;
00044 extern bool cf_OverwriteExistingVariable;
00045 extern CIFile cf_ifile;
00046 
00047 // put true if you want that the config file class check type when you call asFunctions
00048 // (for example, check when you call asInt() that the variable is an int).
00049 // when it's false, the function will convert to the wanted type (if he can)
00050 const bool CheckType = false;
00051 
00052 namespace NLMISC
00053 {
00054 
00055 char *CConfigFile::CVar::TypeName[] = { "Integer", "String", "Float" };
00056 
00057 int CConfigFile::CVar::asInt (int index) const
00058 {
00059         if (CheckType && Type != T_INT) throw EBadType (Name, Type, T_INT);
00060         switch (Type)
00061         {
00062         case T_STRING:
00063                 if (index >= (int)StrValues.size () || index < 0) throw EBadSize (Name, StrValues.size (), index);
00064                 return atoi(StrValues[index].c_str());
00065         case T_REAL:
00066                 if (index >= (int)RealValues.size () || index < 0) throw EBadSize (Name, RealValues.size (), index);
00067                 return (int)RealValues[index];
00068         default:
00069                 if (index >= (int)IntValues.size () || index < 0) throw EBadSize (Name, IntValues.size (), index);
00070                 return IntValues[index];
00071         }
00072 }
00073 
00074 double CConfigFile::CVar::asDouble (int index) const
00075 {
00076         if (CheckType && Type != T_REAL) throw EBadType (Name, Type, T_REAL);
00077         switch (Type)
00078         {
00079         case T_INT:
00080                 if (index >= (int)IntValues.size () || index < 0) throw EBadSize (Name, IntValues.size (), index);
00081                 return (double)IntValues[index];
00082         case T_STRING:
00083                 if (index >= (int)StrValues.size () || index < 0) throw EBadSize (Name, StrValues.size (), index);
00084                 return atof(StrValues[index].c_str());
00085         default:
00086                 if (index >= (int)RealValues.size () || index < 0) throw EBadSize (Name, RealValues.size (), index);
00087                 return RealValues[index];
00088         }
00089 }
00090 
00091 float CConfigFile::CVar::asFloat (int index) const
00092 {
00093         return (float) asDouble (index);
00094 }
00095 
00096 const std::string &CConfigFile::CVar::asString (int index) const
00097 {
00098         if (Type != T_STRING) throw EBadType (Name, Type, T_STRING);
00099         if (index >= (int)StrValues.size () || index < 0) throw EBadSize (Name, StrValues.size (), index);
00100         return StrValues[index];
00101 }
00102 
00103 
00104 
00105 void CConfigFile::CVar::setAsInt (int val, int index)
00106 {
00107         if (Type != T_INT) throw EBadType (Name, Type, T_INT);
00108         else if (index > (int)IntValues.size () || index < 0) throw EBadSize (Name, IntValues.size (), index);
00109         else if (index == (int)IntValues.size ()) IntValues.push_back(val);
00110         else IntValues[index] = val;
00111 }
00112 
00113 void CConfigFile::CVar::setAsDouble (double val, int index)
00114 {
00115         if (Type != T_REAL) throw EBadType (Name, Type, T_REAL);
00116         else if (index > (int)RealValues.size () || index < 0) throw EBadSize (Name, RealValues.size (), index);
00117         else if (index == (int)RealValues.size ()) RealValues.push_back(val);
00118         else RealValues[index] = val;
00119 }
00120 
00121 void CConfigFile::CVar::setAsFloat (float val, int index)
00122 {
00123         setAsDouble (val, index);
00124 }
00125 
00126 void CConfigFile::CVar::setAsString (std::string val, int index)
00127 {
00128         if (Type != T_STRING) throw EBadType (Name, Type, T_STRING);
00129         else if (index > (int)StrValues.size () || index < 0) throw EBadSize (Name, StrValues.size (), index);
00130         else if (index == (int)StrValues.size ()) StrValues.push_back(val);
00131         else StrValues[index] = val;
00132 }
00133 
00134 void CConfigFile::CVar::setAsInt (std::vector<int> vals)
00135 {
00136         if (Type != T_INT) throw EBadType (Name, Type, T_INT);
00137         else IntValues = vals;
00138 }
00139 
00140 void CConfigFile::CVar::setAsDouble (std::vector<double> vals)
00141 {
00142         if (Type != T_REAL) throw EBadType (Name, Type, T_REAL);
00143         else RealValues = vals;
00144 }
00145 
00146 void CConfigFile::CVar::setAsFloat (std::vector<float> vals)
00147 {
00148         if (Type != T_REAL) throw EBadType (Name, Type, T_REAL);
00149         else
00150         {
00151                 RealValues.clear ();
00152                 RealValues.resize (vals.size ());
00153                 for (uint i = 0; i < vals.size (); i++)
00154                         RealValues[i] = (double)vals[i];
00155         }
00156 }
00157 
00158 void CConfigFile::CVar::setAsString (std::vector<std::string> vals)
00159 {
00160         if (Type != T_STRING) throw EBadType (Name, Type, T_STRING);
00161         else StrValues = vals;
00162 }
00163 
00164 bool CConfigFile::CVar::operator==      (const CVar& var) const
00165 {
00166         if (Type == var.Type)
00167         {
00168                 switch (Type)
00169                 {
00170                 case T_INT: return IntValues == var.IntValues; break;
00171                 case T_REAL: return RealValues == var.RealValues; break;
00172                 case T_STRING: return StrValues == var.StrValues; break;
00173                 default: break;
00174                 }
00175         }
00176         return false;
00177 }
00178 
00179 bool CConfigFile::CVar::operator!=      (const CVar& var) const
00180 {
00181         return !(*this==var);
00182 }
00183 
00184 void CConfigFile::CVar::add (const CVar &var)
00185 {
00186         if (Type == var.Type)
00187         {
00188                 switch (Type)
00189                 {
00190                 case T_INT: IntValues.insert (IntValues.end(), var.IntValues.begin(), var.IntValues.end()); break;
00191                 case T_REAL: RealValues.insert (RealValues.end(), var.RealValues.begin(), var.RealValues.end()); break;
00192                 case T_STRING: StrValues.insert (StrValues.end(), var.StrValues.begin(), var.StrValues.end()); break;
00193                 default: break;
00194                 }
00195         }
00196 }
00197 
00198 int CConfigFile::CVar::size () const
00199 {
00200         switch (Type)
00201         {
00202         case T_INT: return IntValues.size ();
00203         case T_REAL: return RealValues.size ();
00204         case T_STRING: return StrValues.size ();
00205         default: return 0;
00206         }
00207 }
00208 
00209 CConfigFile::~CConfigFile ()
00210 {
00211         if (_ConfigFiles == NULL || (*_ConfigFiles).empty ()) return;
00212 
00213         vector<CConfigFile *>::iterator it = find ((*_ConfigFiles).begin (), (*_ConfigFiles).end (), this);
00214         if (it != (*_ConfigFiles).end ())
00215         {
00216                 (*_ConfigFiles).erase (it);
00217         }
00218 
00219         if ((*_ConfigFiles).empty())
00220         {
00221                 delete _ConfigFiles;
00222                 _ConfigFiles = NULL;
00223         }
00224 }
00225 
00226 void CConfigFile::load (const string &fileName)
00227 {
00228         if(fileName.empty())
00229         {
00230                 nlwarning ("Can't load a empty file name configfile");
00231                 return;
00232         }
00233 
00234         _FileName = fileName;
00235 
00236         if (_ConfigFiles == NULL)
00237         {
00238                 _ConfigFiles = new std::vector<CConfigFile *>;
00239         }
00240         (*CConfigFile::_ConfigFiles).push_back (this);
00241         reparse ();
00242 
00243         // If we find a linked config file, load it but don't overload already existant variable
00244         CVar *var = getVarPtr ("RootConfigFilename");
00245         if (var)
00246         {
00247                 string RootConfigFilename = var->asString();
00248                 nlinfo ("RootConfigFilename variable found in the '%s' config file, parse it (%s)", fileName.c_str(), RootConfigFilename.c_str());
00249 
00250                 string path = CFile::getPath(fileName);
00251 
00252                 if (!path.empty())
00253                         path +=  "/";
00254 
00255                 path += RootConfigFilename;
00256 
00257                 reparse (path.c_str());
00258         }
00259 
00260 //      print ();
00261 }
00262 
00263 
00264 bool CConfigFile::loaded()
00265 {
00266         return !CConfigFile::_FileName.empty();
00267 }
00268 
00269 
00270 void CConfigFile::reparse (const char *filename, bool callingCallback)
00271 {
00272         if (filename == NULL)
00273         {
00274                 _LastModified = getLastModified ();
00275 
00276                 nlassert (!_FileName.empty());
00277 
00278                 if (cf_ifile.open (_FileName))
00279                 {
00280                         // if we clear all the array, we'll lost the callback on variable and all information
00281                         //              _Vars.clear();
00282                         cfrestart (NULL);
00283                         cf_OverwriteExistingVariable = true;
00284                         bool parsingOK = (cfparse (&(_Vars)) == 0);
00285                         cf_ifile.close();
00286                         if (!parsingOK)
00287                         {
00288                                 nlwarning ("Parsing error in file %s line %d", _FileName.c_str(), cf_CurrentLine);
00289                                 throw EParseError (_FileName, cf_CurrentLine);
00290                         }
00291                 }
00292                 else
00293                 {
00294                         nlwarning ("ConfigFile '%s' not found in the path '%s'", _FileName.c_str(), CPath::getCurrentPath().c_str());
00295                         throw EFileNotFound (_FileName);
00296                 }
00297 
00298                 /*
00299                 cfin = fopen (_FileName.c_str (), "r");
00300                 if (cfin != NULL)
00301                 {
00302         // if we clear all the array, we'll lost the callback on variable and all information
00303         //              _Vars.clear();
00304                         cfrestart (cfin);
00305                         cf_OverwriteExistingVariable = true;
00306                         bool parsingOK = (cfparse (&(_Vars)) == 0);
00307                         fclose (cfin);
00308                         if (!parsingOK) throw EParseError (_FileName, cf_CurrentLine);
00309                 }
00310                 else
00311                 {
00312                         nlwarning ("ConfigFile '%s' not found in the path '%s'", _FileName.c_str(), CPath::getCurrentPath().c_str());
00313                         throw EFileNotFound (_FileName);
00314                 }
00315                 */
00316         }
00317         else
00318         {
00319                 nlassert (strlen(filename)>0);
00320 
00321                 // load external config filename, don't overwrite existant variable
00322                 if (cf_ifile.open (filename))
00323                 {
00324                         cfrestart (NULL);
00325                         cf_OverwriteExistingVariable = false;
00326                         bool parsingOK = (cfparse (&(_Vars)) == 0);
00327                         cf_ifile.close ();
00328                         if (!parsingOK)
00329                         {
00330                                 nlwarning ("Parsing error in file %s line %d", filename, cf_CurrentLine);
00331                                 throw EParseError (filename, cf_CurrentLine);
00332                         }
00333                 }
00334                 else
00335                 {
00336                         nlwarning ("RootConfigFilename '%s' not found", _FileName.c_str());
00337                 }
00338 
00339 /*              cfin = fopen (filename, "r");
00340                 if (cfin != NULL)
00341                 {
00342                         cfrestart (cfin);
00343                         cf_OverwriteExistingVariable = false;
00344                         bool parsingOK = (cfparse (&(_Vars)) == 0);
00345                         fclose (cfin);
00346                         if (!parsingOK) throw EParseError (_FileName, cf_CurrentLine);
00347                 }
00348                 else
00349                 {
00350                         nlwarning ("RootConfigFilename '%s' not found", _FileName.c_str());
00351                 }
00352 */      }
00353 
00354         if (callingCallback)
00355         {
00356                 nlwarning("Callback ptr : %p", _Callback);
00357                 if (_Callback != NULL) 
00358                         _Callback();
00359         }
00360 }
00361 
00362 
00363 
00364 CConfigFile::CVar &CConfigFile::getVar (const std::string &varName)
00365 {
00366         uint i;
00367         for (i = 0; i < _Vars.size(); i++)
00368         {
00369                 // the type could be T_UNKNOWN if we add a callback on this name but this var is not in the config file
00370                 if (_Vars[i].Name == varName && (_Vars[i].Type != CVar::T_UNKNOWN || _Vars[i].Comp))
00371                 {
00372                         return _Vars[i];
00373                         break;
00374                 }
00375         }
00376 
00377         // if not found, add it in the array if necessary
00378         for (i = 0; i < UnknownVariables.size(); i++)
00379                 if(UnknownVariables[i] == varName)
00380                         break;
00381         if (i == UnknownVariables.size())
00382                 UnknownVariables.push_back(varName);
00383 
00384         throw EUnknownVar (_FileName, varName);
00385 }
00386 
00387 
00388 CConfigFile::CVar *CConfigFile::getVarPtr (const std::string &varName)
00389 {
00390         uint i;
00391         for (i = 0; i < _Vars.size(); i++)
00392         {
00393                 // the type could be T_UNKNOWN if we add a callback on this name but this var is not in the config file
00394                 if (_Vars[i].Name == varName && (_Vars[i].Type != CVar::T_UNKNOWN || _Vars[i].Comp))
00395                 {
00396                         return &(_Vars[i]);
00397                 }
00398         }
00399 
00400         // if not found, add it in the array if necessary
00401         for (i = 0; i < UnknownVariables.size(); i++)
00402                 if(UnknownVariables[i] == varName)
00403                         break;
00404         if (i == UnknownVariables.size())
00405                 UnknownVariables.push_back(varName);
00406 
00407         return NULL;
00408 }
00409 
00410 bool CConfigFile::exists (const std::string &varName)
00411 {
00412         for (uint i = 0; i < _Vars.size(); i++)
00413         {
00414                 // the type could be T_UNKNOWN if we add a callback on this name but this var is not in the config file
00415                 if (_Vars[i].Name == varName && (_Vars[i].Type != CVar::T_UNKNOWN || _Vars[i].Comp))
00416                 {
00417                         return true;
00418                 }
00419         }
00420         return false;
00421 }
00422 
00423 void CConfigFile::save () const
00424 {
00425         FILE *fp = fopen (_FileName.c_str (), "w");
00426         if (fp == NULL)
00427         {
00428                 nlwarning ("Couldn't create %s file", _FileName.c_str ());
00429                 return;
00430         }
00431 
00432         for(int i = 0; i < (int)_Vars.size(); i++)
00433         {
00434                 if (_Vars[i].Comp)
00435                 {
00436                         fprintf(fp, "%-20s = { ", _Vars[i].Name.c_str());
00437                         switch (_Vars[i].Type)
00438                         {
00439                         case CConfigFile::CVar::T_INT:
00440                         {
00441                                 for (int it=0; it < (int)_Vars[i].IntValues.size(); it++)
00442                                 {
00443                                         fprintf(fp, "%d%s", _Vars[i].IntValues[it], it<(int)_Vars[i].IntValues.size()-1?", ":" ");
00444                                 }
00445                                 break;
00446                         }
00447                         case CConfigFile::CVar::T_STRING:
00448                         {
00449                                 for (int st=0; st < (int)_Vars[i].StrValues.size(); st++)
00450                                 {
00451                                         fprintf(fp, "\"%s\"%s", _Vars[i].StrValues[st].c_str(), st<(int)_Vars[i].StrValues.size()-1?", ":" ");
00452                                 }
00453                                 break;
00454                         }
00455                         case CConfigFile::CVar::T_REAL:
00456                         {
00457                                 for (int rt=0; rt < (int)_Vars[i].RealValues.size(); rt++)
00458                                 {
00459                                         fprintf(fp, "%.10f%s", _Vars[i].RealValues[rt], rt<(int)_Vars[i].RealValues.size()-1?", ":" ");
00460                                 }
00461                                 break;
00462                         }
00463                         default: break;
00464                         }
00465                         fprintf(fp, "};\n");
00466                 }
00467                 else
00468                 {
00469                         switch (_Vars[i].Type)
00470                         {
00471                         case CConfigFile::CVar::T_INT:
00472                                 fprintf(fp, "%-20s = %d;\n", _Vars[i].Name.c_str(), _Vars[i].IntValues[0]);
00473                                 break;
00474                         case CConfigFile::CVar::T_STRING:
00475                                 fprintf(fp, "%-20s = \"%s\";\n", _Vars[i].Name.c_str(), _Vars[i].StrValues[0].c_str());
00476                                 break;
00477                         case CConfigFile::CVar::T_REAL:
00478                                 fprintf(fp, "%-20s = %.10f;\n", _Vars[i].Name.c_str(), _Vars[i].RealValues[0]);
00479                                 break;
00480                         default: break;
00481                         }
00482                 }
00483         }
00484         fclose (fp);
00485 }
00486 
00487 void CConfigFile::print () const
00488 {
00489         print(InfoLog);
00490 }
00491 
00492 void CConfigFile::print (CLog *log) const
00493 {
00494         createDebug ();
00495 
00496         log->displayRawNL ("ConfigFile %s have %d variables:", _FileName.c_str(), _Vars.size());
00497         log->displayRawNL ("------------------------------------------------------");
00498         for(int i = 0; i < (int)_Vars.size(); i++)
00499         {
00500                 log->displayRaw ((_Vars[i].Callback==NULL)?"   ":"CB ");
00501                 if (_Vars[i].Comp)
00502                 {
00503                         switch (_Vars[i].Type)
00504                         {
00505                         case CConfigFile::CVar::T_INT:
00506                         {
00507                                 log->displayRaw ("%-20s { ", _Vars[i].Name.c_str());
00508                                 for (int it=0; it < (int)_Vars[i].IntValues.size(); it++)
00509                                 {
00510                                         log->displayRaw ("'%d' ", _Vars[i].IntValues[it]);
00511                                 }
00512                                 log->displayRawNL ("}");
00513                                 break;
00514                         }
00515                         case CConfigFile::CVar::T_STRING:
00516                         {
00517                                 log->displayRaw ("%-20s { ", _Vars[i].Name.c_str());
00518                                 for (int st=0; st < (int)_Vars[i].StrValues.size(); st++)
00519                                 {
00520                                         log->displayRaw ("\"%s\" ", _Vars[i].StrValues[st].c_str());
00521                                 }
00522                                 log->displayRawNL ("}");
00523                                 break;
00524                         }
00525                         case CConfigFile::CVar::T_REAL:
00526                         {
00527                                 log->displayRaw ("%-20s { " , _Vars[i].Name.c_str());
00528                                 for (int rt=0; rt < (int)_Vars[i].RealValues.size(); rt++)
00529                                 {
00530                                         log->displayRaw ("`%f` ", _Vars[i].RealValues[rt]);
00531                                 }
00532                                 log->displayRawNL ("}");
00533                                 break;
00534                         }
00535                         case CConfigFile::CVar::T_UNKNOWN:
00536                         {
00537                                  log->displayRawNL ("%-20s { }" , _Vars[i].Name.c_str());
00538                                 break;
00539                         }
00540                         default:
00541                         {
00542                                 log->displayRawNL ("%-20s <default case>" , _Vars[i].Name.c_str());
00543                                 break;
00544                         }
00545                         }
00546                 }
00547                 else
00548                 {
00549                         switch (_Vars[i].Type)
00550                         {
00551                         case CConfigFile::CVar::T_INT:
00552                                 log->displayRawNL ("%-20s '%d'", _Vars[i].Name.c_str(), _Vars[i].IntValues[0]);
00553                                 break;
00554                         case CConfigFile::CVar::T_STRING:
00555                                 log->displayRawNL ("%-20s \"%s\"", _Vars[i].Name.c_str(), _Vars[i].StrValues[0].c_str());
00556                                 break;
00557                         case CConfigFile::CVar::T_REAL:
00558                                 log->displayRawNL ("%-20s `%f`", _Vars[i].Name.c_str(), _Vars[i].RealValues[0]);
00559                                 break;
00560                         default:
00561                         {
00562                                 log->displayRawNL ("%-20s <default case>" , _Vars[i].Name.c_str());
00563                                 break;
00564                         }
00565                         }
00566                 }
00567         }
00568 }
00569 
00570 void CConfigFile::setCallback (void (*cb)())
00571 {
00572         _Callback = cb;
00573 }
00574 
00575 void CConfigFile::setCallback (const string &VarName, void (*cb)(CConfigFile::CVar &var))
00576 {
00577         for (vector<CVar>::iterator it = _Vars.begin (); it != _Vars.end (); it++)
00578         {
00579                 if (VarName == (*it).Name)
00580                 {
00581                         (*it).Callback = cb;
00582                         return;
00583                 }
00584         }
00585         // VarName doesn't exist, add it now for the futur
00586         CVar Var;
00587         Var.Name = VarName;
00588         Var.Callback = cb;
00589         Var.Type = CVar::T_UNKNOWN;
00590         _Vars.push_back (Var);
00591 }
00592 
00593 void CConfigFile::setLastModifiedNow ()
00594 {
00595         _LastModified = getLastModified ();
00596 }
00597 
00598 
00599 // ***************************************************************************
00600 
00601 
00602 vector<CConfigFile *> *CConfigFile::_ConfigFiles = NULL;
00603 
00604 uint32  CConfigFile::_Timeout = 1000;
00605 
00606 uint32 CConfigFile::getLastModified ()
00607 {
00608         uint pos;
00609         string fn;
00610         if ((pos=_FileName.find('@')) != string::npos)
00611         {
00612                 fn = _FileName.substr (0, pos);
00613         }
00614         else
00615         {
00616                 fn = _FileName;
00617         }
00618 #if defined (NL_OS_WINDOWS)
00619         struct _stat buf;
00620         int result = _stat (fn.c_str (), &buf);
00621 #elif defined (NL_OS_UNIX)
00622         struct stat buf;
00623         int result = stat (fn.c_str (), &buf);
00624 #endif
00625 
00626         if (result != 0) return 0;
00627         else return buf.st_mtime;
00628 }
00629 
00630 
00631 void CConfigFile::checkConfigFiles ()
00632 {
00633         if (_ConfigFiles == NULL) return;
00634 
00635         static time_t LastCheckTime = time (NULL);
00636         if (_Timeout > 0 && (float)(time (NULL) - LastCheckTime)*1000.0f < (float)_Timeout) return;
00637 
00638         LastCheckTime = time (NULL);
00639 
00640         for (vector<CConfigFile *>::iterator it = (*_ConfigFiles).begin (); it != (*_ConfigFiles).end (); it++)
00641         {
00642                 if ((*it)->_LastModified != (*it)->getLastModified ())
00643                 {
00644                         try
00645                         {
00646                                 (*it)->reparse ();
00647                         }
00648                         catch (EConfigFile &e)
00649                         {
00650                                 nlwarning ("Exception will rereading modified config file: %s", e.what ());
00651                         }
00652                 }
00653         }
00654 }
00655 
00656 void CConfigFile::setTimeout (uint32 timeout)
00657 {
00658         nlassert (timeout>=0);
00659         _Timeout = timeout;
00660 }
00661 
00662 } // NLMISC