00001
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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 *);
00041 extern int cfparse (void *);
00042
00043 extern int cf_CurrentLine;
00044 extern bool cf_OverwriteExistingVariable;
00045 extern CIFile cf_ifile;
00046
00047
00048
00049
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
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
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
00281
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
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316 }
00317 else
00318 {
00319 nlassert (strlen(filename)>0);
00320
00321
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
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
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
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
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
00394 if (_Vars[i].Name == varName && (_Vars[i].Type != CVar::T_UNKNOWN || _Vars[i].Comp))
00395 {
00396 return &(_Vars[i]);
00397 }
00398 }
00399
00400
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
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
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 }