Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / misc / config_file / config_file.cpp
blobb1ef3bef653dd4af72d7bc2f1748868bfdd10feb
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "../stdmisc.h"
18 #include "nel/misc/config_file.h"
20 #include <ctime>
21 #include <sys/types.h>
22 #include <sys/stat.h>
24 #include "nel/misc/file.h"
25 #include "nel/misc/debug.h"
26 #include "nel/misc/path.h"
27 #include "nel/misc/i18n.h"
28 #include "nel/misc/mem_stream.h"
29 #include "locale.h"
31 #ifdef DEBUG_NEW
32 #define new DEBUG_NEW
33 #endif
35 using namespace std;
36 using namespace NLMISC;
38 extern void cfrestart (FILE *); // used to reinit the file
39 extern int cfparse (void *); // used to parse the file
40 //extern FILE *cfin;
41 extern int cf_CurrentLine;
42 extern char *cf_CurrentFile;
43 extern bool cf_Ignore;
44 extern bool cf_OverwriteExistingVariable;
45 extern CMemStream cf_ifile;
47 // put true if you want that the config file class check type when you call asFunctions
48 // (for example, check when you call asInt() that the variable is an int).
49 // when it's false, the function will convert to the wanted type (if he can)
50 const bool CheckType = false;
51 bool LoadRoot = false;
53 namespace NLMISC
56 const char *CConfigFile::CVar::TypeName[] = { "Integer", "String", "Float", "Boolean" };
58 int CConfigFile::CVar::asInt (int index) const
60 if (CheckType && Type != T_INT) throw EBadType (Name, Type, T_INT);
61 switch (Type)
63 case T_STRING:
65 if (index >= (int)StrValues.size () || index < 0) throw EBadSize (Name, (int)StrValues.size (), index);
66 int ret = 0;
67 NLMISC::fromString(StrValues[index], ret);
68 return ret;
70 case T_REAL:
71 if (index >= (int)RealValues.size () || index < 0) throw EBadSize (Name, (int)RealValues.size (), index);
72 return (int)RealValues[index];
73 default:
74 if (index >= (int)IntValues.size () || index < 0) throw EBadSize (Name, (int)IntValues.size (), index);
75 return IntValues[index];
79 double CConfigFile::CVar::asDouble (int index) const
81 if (CheckType && Type != T_REAL) throw EBadType (Name, Type, T_REAL);
82 switch (Type)
84 case T_INT:
85 if (index >= (int)IntValues.size () || index < 0) throw EBadSize (Name, (int)IntValues.size (), index);
86 return (double)IntValues[index];
87 case T_STRING:
89 if (index >= (int)StrValues.size () || index < 0) throw EBadSize (Name, (int)StrValues.size (), index);
90 double val;
91 NLMISC::fromString(StrValues[index], val);
92 return val;
94 default:
95 if (index >= (int)RealValues.size () || index < 0) throw EBadSize (Name, (int)RealValues.size (), index);
96 return RealValues[index];
100 float CConfigFile::CVar::asFloat (int index) const
102 return (float) asDouble (index);
105 std::string CConfigFile::CVar::asString (int index) const
107 if (CheckType && Type != T_STRING) throw EBadType (Name, Type, T_STRING);
108 switch (Type)
110 case T_INT:
111 if (index >= (int)IntValues.size () || index < 0) throw EBadSize (Name, (int)IntValues.size (), index);
112 return toString(IntValues[index]);
113 case T_REAL:
114 if (index >= (int)RealValues.size () || index < 0) throw EBadSize (Name, (int)RealValues.size (), index);
115 return toString(RealValues[index]);
116 default:
117 if (index >= (int)StrValues.size () || index < 0) throw EBadSize (Name, (int)StrValues.size (), index);
118 return StrValues[index];
122 bool CConfigFile::CVar::asBool (int index) const
124 switch (Type)
126 case T_STRING:
127 if (index >= (int)StrValues.size () || index < 0) throw EBadSize (Name, (int)StrValues.size (), index);
128 if(StrValues[index] == "true")
130 return true;
132 else
134 return false;
136 case T_REAL:
137 if (index >= (int)RealValues.size () || index < 0) throw EBadSize (Name, (int)RealValues.size (), index);
138 if ((int)RealValues[index] == 1)
140 return true;
142 else
144 return false;
146 default:
147 if (index >= (int)IntValues.size () || index < 0) throw EBadSize (Name, (int)IntValues.size (), index);
148 if (IntValues[index] == 1)
150 return true;
152 else
154 return false;
159 void CConfigFile::CVar::setAsInt (int val, int index)
161 if (Type != T_INT) throw EBadType (Name, Type, T_INT);
162 else if (index > (int)IntValues.size () || index < 0) throw EBadSize (Name, (int)IntValues.size (), index);
163 else if (index == (int)IntValues.size ()) IntValues.push_back(val);
164 else IntValues[index] = val;
165 Root = false;
168 void CConfigFile::CVar::setAsDouble (double val, int index)
170 if (Type != T_REAL) throw EBadType (Name, Type, T_REAL);
171 else if (index > (int)RealValues.size () || index < 0) throw EBadSize (Name, (int)RealValues.size (), index);
172 else if (index == (int)RealValues.size ()) RealValues.push_back(val);
173 else RealValues[index] = val;
174 Root = false;
177 void CConfigFile::CVar::setAsFloat (float val, int index)
179 setAsDouble (val, index);
182 void CConfigFile::CVar::setAsString (const std::string &val, int index)
184 if (Type != T_STRING) throw EBadType (Name, Type, T_STRING);
185 else if (index > (int)StrValues.size () || index < 0) throw EBadSize (Name, (int)StrValues.size (), index);
186 else if (index == (int)StrValues.size ()) StrValues.push_back(val);
187 else StrValues[index] = val;
188 Root = false;
191 void CConfigFile::CVar::forceAsInt (int val)
193 Type= T_INT;
194 IntValues.resize(1);
195 RealValues.clear();
196 StrValues.clear();
197 IntValues[0]= val;
198 Root = false;
201 void CConfigFile::CVar::forceAsDouble (double val)
203 Type= T_REAL;
204 IntValues.clear();
205 RealValues.resize(1);
206 StrValues.clear();
207 RealValues[0]= val;
208 Root = false;
211 void CConfigFile::CVar::forceAsString (const std::string &val)
213 Type= T_STRING;
214 IntValues.clear();
215 RealValues.clear();
216 StrValues.resize(1);
217 StrValues[0]= val;
218 Root = false;
221 void CConfigFile::CVar::setAsInt (const std::vector<int> &vals)
223 if (Type != T_INT) throw EBadType (Name, Type, T_INT);
224 else IntValues = vals;
225 Root = false;
228 void CConfigFile::CVar::setAsDouble (const std::vector<double> &vals)
230 if (Type != T_REAL) throw EBadType (Name, Type, T_REAL);
231 else RealValues = vals;
232 Root = false;
235 void CConfigFile::CVar::setAsFloat (const std::vector<float> &vals)
237 if (Type != T_REAL) throw EBadType (Name, Type, T_REAL);
238 else
240 RealValues.clear ();
241 RealValues.resize (vals.size ());
242 for (uint i = 0; i < vals.size (); i++)
243 RealValues[i] = (double)vals[i];
245 Root = false;
248 void CConfigFile::CVar::setAsString (const std::vector<std::string> &vals)
250 if (Type != T_STRING) throw EBadType (Name, Type, T_STRING);
251 else StrValues = vals;
252 Root = false;
255 bool CConfigFile::CVar::operator== (const CVar& var) const
257 if (Type == var.Type)
259 switch (Type)
261 case T_INT: return IntValues == var.IntValues; break;
262 case T_REAL: return RealValues == var.RealValues; break;
263 case T_STRING: return StrValues == var.StrValues; break;
264 default: break;
267 return false;
270 bool CConfigFile::CVar::operator!= (const CVar& var) const
272 return !(*this==var);
275 void CConfigFile::CVar::add (const CVar &var)
277 if (Type == var.Type)
279 switch (Type)
281 case T_INT: IntValues.insert (IntValues.end(), var.IntValues.begin(), var.IntValues.end()); break;
282 case T_REAL: RealValues.insert (RealValues.end(), var.RealValues.begin(), var.RealValues.end()); break;
283 case T_STRING: StrValues.insert (StrValues.end(), var.StrValues.begin(), var.StrValues.end()); break;
284 default: break;
289 uint CConfigFile::CVar::size () const
291 switch (Type)
293 case T_INT: return (uint)IntValues.size ();
294 case T_REAL: return (uint)RealValues.size ();
295 case T_STRING: return (uint)StrValues.size ();
296 default: return 0;
300 CConfigFile::~CConfigFile ()
302 if (_ConfigFiles == NULL || (*_ConfigFiles).empty ()) return;
304 vector<CConfigFile *>::iterator it = find ((*_ConfigFiles).begin (), (*_ConfigFiles).end (), this);
305 if (it != (*_ConfigFiles).end ())
307 (*_ConfigFiles).erase (it);
310 if ((*_ConfigFiles).empty())
312 delete _ConfigFiles;
313 _ConfigFiles = NULL;
317 void CConfigFile::load (const string &fileName, bool lookupPaths )
319 char *locale = setlocale(LC_NUMERIC, NULL);
321 if (!locale || strcmp(locale, "C"))
323 nlerror("Numeric locale not defined to C, an external library possibly redefined it!");
326 if(fileName.empty())
328 nlwarning ("CF: Can't load a empty file name configfile");
329 return;
332 FileNames.clear ();
333 FileNames.push_back (fileName);
335 if (_ConfigFiles == NULL)
337 _ConfigFiles = new std::vector<CConfigFile *>;
339 (*CConfigFile::_ConfigFiles).push_back (this);
340 reparse (lookupPaths);
342 /* _FileName.clear ();
343 _FileName.push_back (fileName);
345 if (_ConfigFiles == NULL)
347 _ConfigFiles = new std::vector<CConfigFile *>;
349 (*CConfigFile::_ConfigFiles).push_back (this);
350 reparse ();
352 // If we find a linked config file, load it but don't overload already existing variable
353 CVar *var = getVarPtr ("RootConfigFilename");
354 if (var)
356 string RootConfigFilename = var->asString();
357 nlinfo ("RootConfigFilename variable found in the '%s' config file, parse it (%s)", fileName.c_str(), RootConfigFilename.c_str());
359 string path = CFile::getPath(fileName);
361 if (!path.empty())
362 path += "/";
364 path += RootConfigFilename;
366 reparse (path.c_str());
369 // print ();
372 bool CConfigFile::loaded()
374 return !CConfigFile::FileNames.empty();
377 uint32 CConfigFile::getVarCount()
379 return (uint32)_Vars.size();
383 void CConfigFile::reparse (bool lookupPaths)
385 if (FileNames.empty())
387 nlwarning ("CF: Can't reparse config file because file name is empty");
388 return;
391 string fn = FileNames[0];
393 FileNames.clear ();
394 LastModified.clear ();
396 // clearVars ();
398 while (!fn.empty())
400 if (lookupPaths)
402 fn = CPath::lookup(fn, true);
404 else
406 fn = NLMISC::CPath::getFullPath(fn, false);
408 nldebug ("CF: Adding config file '%s' in the config file", fn.c_str());
409 FileNames.push_back (fn);
410 LastModified.push_back (CFile::getFileModificationDate(fn));
412 if (!CPath::lookup(fn, false).empty())
414 ucstring content;
415 CI18N::readTextFile(fn, content, true, true);
416 string utf8 = content.toUtf8();
418 CMemStream stream;
419 stream.serialBuffer((uint8*)(utf8.data()), (uint)utf8.size());
420 cf_ifile = stream;
421 if (!cf_ifile.isReading())
423 cf_ifile.invert();
426 cfrestart (NULL);
427 cf_CurrentLine = 0;
428 cf_CurrentFile = NULL;
429 cf_Ignore = false;
430 cf_OverwriteExistingVariable = (FileNames.size()==1);
431 LoadRoot = (FileNames.size()>1);
432 bool parsingOK = (cfparse (&(_Vars)) == 0);
433 // cf_ifile.close();
434 if (!parsingOK)
436 // write the result of preprocessing in a temp file
437 string debugFileName;
438 debugFileName += "debug_";
439 debugFileName += CFile::getFilename(fn);
441 CI18N::writeTextFile(debugFileName, content, true);
442 nlwarning ("CF: Parsing error in file %s line %d, look in '%s' for a preprocessed version of the config file",
443 cf_CurrentFile,
444 cf_CurrentLine,
445 debugFileName.c_str());
446 throw EParseError (fn, cf_CurrentLine);
449 if (cf_CurrentFile != NULL)
450 free(cf_CurrentFile);
452 // reset all 'FromLocalFile' flag on created vars before reading next root cfg
453 for (uint i=0; i<_Vars.size(); ++i)
455 _Vars[i].FromLocalFile = false;
458 else
460 nlwarning ("CF: Config file '%s' not found in the path '%s'", fn.c_str(), CPath::getCurrentPath().c_str());
461 throw EFileNotFound (fn);
463 // cf_ifile.close ();
464 cf_ifile.clear();
466 // If we find a linked config file, load it but don't overload already existing variable
467 CVar *var = getVarPtr ("RootConfigFilename");
468 if (var)
470 string RootConfigFilename = var->asString();
472 if (!NLMISC::CFile::fileExists(RootConfigFilename))
474 // file is not found, try with the path of the master cfg
475 string path = NLMISC::CPath::standardizePath (NLMISC::CFile::getPath(FileNames[0]));
476 RootConfigFilename = path + RootConfigFilename;
479 RootConfigFilename = NLMISC::CPath::getFullPath(RootConfigFilename, false);
481 if (RootConfigFilename != fn)
483 nlinfo ("CF: RootConfigFilename variable found in the '%s' config file, parse the root config file '%s'", fn.c_str(), RootConfigFilename.c_str());
484 fn = RootConfigFilename;
486 else
487 fn.clear ();
489 else
490 fn.clear ();
493 if (_Callback != NULL)
494 _Callback();
496 /* if (filename == NULL)
498 _LastModified = getLastModified ();
500 nlassert (!_FileName.empty());
502 if (cf_ifile.open (_FileName[0]))
504 // if we clear all the array, we'll lost the callback on variable and all information
505 // _Vars.clear();
506 cfrestart (NULL);
507 cf_CurrentLine = 1;
508 cf_Ignore = false;
509 cf_OverwriteExistingVariable = true;
510 LoadRoot = false;
511 bool parsingOK = (cfparse (&(_Vars)) == 0);
512 cf_ifile.close();
513 if (!parsingOK)
515 nlwarning ("Parsing error in file %s line %d", _FileName.c_str(), cf_CurrentLine);
516 throw EParseError (_FileName, cf_CurrentLine);
519 else
521 nlwarning ("ConfigFile '%s' not found in the path '%s'", _FileName.c_str(), CPath::getCurrentPath().c_str());
522 throw EFileNotFound (_FileName);
525 else
527 nlassert (strlen(filename)>0);
529 // load external config filename, don't overwrite existing variable
530 if (cf_ifile.open (filename))
532 cfrestart (NULL);
533 cf_CurrentLine = 1;
534 cf_Ignore = false;
535 cf_OverwriteExistingVariable = false;
536 LoadRoot = true;
537 bool parsingOK = (cfparse (&(_Vars)) == 0);
538 cf_ifile.close ();
539 if (!parsingOK)
541 nlwarning ("Parsing error in file %s line %d", filename, cf_CurrentLine);
542 throw EParseError (filename, cf_CurrentLine);
545 else
547 nlwarning ("RootConfigFilename '%s' not found", _FileName.c_str());
551 if (callingCallback)
553 if (_Callback != NULL)
554 _Callback();
562 CConfigFile::CVar &CConfigFile::getVar (const std::string &varName)
564 CVar *var = getVarPtr (varName);
565 if (var == 0)
566 throw EUnknownVar (getFilename(), varName);
567 else
568 return *var;
572 CConfigFile::CVar *CConfigFile::getVarPtr (const std::string &varName)
574 uint i;
575 for (i = 0; i < _Vars.size(); i++)
577 // the type could be T_UNKNOWN if we add a callback on this name but this var is not in the config file
578 if (_Vars[i].Name == varName && (_Vars[i].Type != CVar::T_UNKNOWN || _Vars[i].Comp))
579 return &(_Vars[i]);
582 // if not found, add it in the array if necessary
583 for (i = 0; i < UnknownVariables.size(); i++)
584 if(UnknownVariables[i] == varName)
585 break;
586 if (i == UnknownVariables.size())
587 UnknownVariables.push_back(varName);
589 return NULL;
592 bool CConfigFile::exists (const std::string &varName)
594 for (uint i = 0; i < _Vars.size(); i++)
596 // the type could be T_UNKNOWN if we add a callback on this name but this var is not in the config file
597 if (_Vars[i].Name == varName && (_Vars[i].Type != CVar::T_UNKNOWN || _Vars[i].Comp))
599 return true;
602 return false;
605 void CConfigFile::save () const
607 char *locale = setlocale(LC_NUMERIC, NULL);
609 if (!locale || strcmp(locale, "C"))
611 nlerror("Numeric locale not defined to C, an external library possibly redefined it!");
614 FILE *fp = nlfopen (getFilename(), "w");
615 if (fp == NULL)
617 nlwarning ("CF: Couldn't create %s file", getFilename().c_str ());
618 return;
621 // write the UTF-8 bom in order to be able to re-read a config file with
622 // unicode content.
623 /* ace: we need to test this before commit it
624 static char utf8Header[] = {char(0xef), char(0xbb), char(0xbf), 0};
625 fprintf(fp, utf8Header);
628 for(int i = 0; i < (int)_Vars.size(); i++)
630 // Not a root value
631 if (!_Vars[i].Root)
633 if (_Vars[i].Comp)
635 fprintf(fp, "%-20s = {", _Vars[i].Name.c_str());
636 switch (_Vars[i].Type)
638 case CConfigFile::CVar::T_INT:
640 for (int it=0; it < (int)_Vars[i].IntValues.size(); it++)
642 if (it%_Vars[i].SaveWrap == 0)
644 fprintf(fp, "\n\t");
646 fprintf(fp, "%d%s", _Vars[i].IntValues[it], it<(int)_Vars[i].IntValues.size()-1?", ":" ");
648 break;
650 case CConfigFile::CVar::T_STRING:
652 for (int st=0; st < (int)_Vars[i].StrValues.size(); st++)
654 if (st%_Vars[i].SaveWrap == 0)
656 fprintf(fp, "\n\t");
658 fprintf(fp, "\"%s\"%s", _Vars[i].StrValues[st].c_str(), st<(int)_Vars[i].StrValues.size()-1?", ":" ");
660 break;
662 case CConfigFile::CVar::T_REAL:
664 for (int rt=0; rt < (int)_Vars[i].RealValues.size(); rt++)
666 if (rt%_Vars[i].SaveWrap == 0)
668 fprintf(fp, "\n\t");
670 fprintf(fp, "%.10f%s", _Vars[i].RealValues[rt], rt<(int)_Vars[i].RealValues.size()-1?", ":" ");
672 break;
674 default: break;
676 fprintf(fp, "\n};\n");
678 else
680 switch (_Vars[i].Type)
682 case CConfigFile::CVar::T_INT:
683 fprintf(fp, "%-20s = %d;\n", _Vars[i].Name.c_str(), _Vars[i].IntValues[0]);
684 break;
685 case CConfigFile::CVar::T_STRING:
686 fprintf(fp, "%-20s = \"%s\";\n", _Vars[i].Name.c_str(), _Vars[i].StrValues[0].c_str());
687 break;
688 case CConfigFile::CVar::T_REAL:
689 fprintf(fp, "%-20s = %.10f;\n", _Vars[i].Name.c_str(), _Vars[i].RealValues[0]);
690 break;
691 default: break;
696 fclose (fp);
699 void CConfigFile::display () const
701 display (InfoLog);
704 void CConfigFile::display (CLog *log) const
706 createDebug ();
708 log->displayRawNL ("Config file %s have %d variables and %d root config file:", getFilename().c_str(), _Vars.size(), FileNames.size()-1);
709 log->displayRaw ("Root config files: ");
710 for(int i = 1; i < (int)FileNames.size(); i++)
712 log->displayRaw (FileNames[i].c_str());
714 log->displayRawNL ("");
715 log->displayRawNL ("------------------------------------------------------");
716 for(int i = 0; i < (int)_Vars.size(); i++)
718 log->displayRaw ((_Vars[i].Callback==NULL)?" ":"CB ");
719 log->displayRaw ((_Vars[i].Root)?"Root ":" ");
720 if (_Vars[i].Comp)
722 switch (_Vars[i].Type)
724 case CConfigFile::CVar::T_INT:
726 log->displayRaw ("%-20s { ", _Vars[i].Name.c_str());
727 for (int it=0; it < (int)_Vars[i].IntValues.size(); it++)
729 log->displayRaw ("'%d' ", _Vars[i].IntValues[it]);
731 log->displayRawNL ("}");
732 break;
734 case CConfigFile::CVar::T_STRING:
736 log->displayRaw ("%-20s { ", _Vars[i].Name.c_str());
737 for (int st=0; st < (int)_Vars[i].StrValues.size(); st++)
739 log->displayRaw ("\"%s\" ", _Vars[i].StrValues[st].c_str());
741 log->displayRawNL ("}");
742 break;
744 case CConfigFile::CVar::T_REAL:
746 log->displayRaw ("%-20s { " , _Vars[i].Name.c_str());
747 for (int rt=0; rt < (int)_Vars[i].RealValues.size(); rt++)
749 log->displayRaw ("`%f` ", _Vars[i].RealValues[rt]);
751 log->displayRawNL ("}");
752 break;
754 case CConfigFile::CVar::T_UNKNOWN:
756 log->displayRawNL ("%-20s { }" , _Vars[i].Name.c_str());
757 break;
759 default:
761 log->displayRawNL ("%-20s <default case comp> (%d)" , _Vars[i].Name.c_str(), _Vars[i].Type);
762 break;
766 else
768 switch (_Vars[i].Type)
770 case CConfigFile::CVar::T_INT:
771 log->displayRawNL ("%-20s '%d'", _Vars[i].Name.c_str(), _Vars[i].IntValues[0]);
772 break;
773 case CConfigFile::CVar::T_STRING:
774 log->displayRawNL ("%-20s \"%s\"", _Vars[i].Name.c_str(), _Vars[i].StrValues[0].c_str());
775 break;
776 case CConfigFile::CVar::T_REAL:
777 log->displayRawNL ("%-20s `%f`", _Vars[i].Name.c_str(), _Vars[i].RealValues[0]);
778 break;
779 case CConfigFile::CVar::T_UNKNOWN:
780 log->displayRawNL ("%-20s <Unknown>", _Vars[i].Name.c_str());
781 break;
782 default:
784 log->displayRawNL ("%-20s <default case> (%d)" , _Vars[i].Name.c_str(), _Vars[i].Type);
785 break;
792 void CConfigFile::setCallback (void (*cb)())
794 _Callback = cb;
795 if( !FileNames.empty() )
796 nlinfo ("CF: Setting callback to reload the file '%s' when modified externally", getFilename().c_str());
799 void CConfigFile::setCallback (const string &VarName, void (*cb)(CConfigFile::CVar &var))
801 for (vector<CVar>::iterator it = _Vars.begin (); it != _Vars.end (); it++)
803 if (VarName == (*it).Name)
805 (*it).Callback = cb;
806 //nldebug("CF: Setting callback to reload the variable '%s' in the file '%s' when modified externally", VarName.c_str(), getFilename().c_str());
807 return;
810 // VarName doesn't exist, add it now for the future
811 CVar Var;
812 Var.Name = VarName;
813 Var.Callback = cb;
814 Var.Type = CVar::T_UNKNOWN;
815 Var.Comp = false;
816 _Vars.push_back (Var);
817 //nldebug("CF: Setting callback to reload the variable '%s' in the file '%s' when modified externally (currently unknown)", VarName.c_str(), getFilename().c_str());
820 // ***************************************************************************
823 vector<CConfigFile *> *CConfigFile::_ConfigFiles = NULL;
825 uint32 CConfigFile::_Timeout = 1000;
827 void CConfigFile::checkConfigFiles ()
829 if (_ConfigFiles == NULL) return;
831 static time_t LastCheckTime = time (NULL);
832 if (_Timeout > 0 && (float)(time (NULL) - LastCheckTime)*1000.0f < (float)_Timeout) return;
834 LastCheckTime = time (NULL);
836 bool needReparse;
837 for (vector<CConfigFile *>::iterator it = (*_ConfigFiles).begin (); it != (*_ConfigFiles).end (); it++)
839 needReparse = false;
840 nlassert ((*it)->FileNames.size() == (*it)->LastModified.size());
841 for (uint i = 0; i < (*it)->FileNames.size(); i++)
843 if ((*it)->LastModified[i] != CFile::getFileModificationDate((*it)->FileNames[i]))
845 needReparse = true;
846 (*it)->LastModified[i] = CFile::getFileModificationDate((*it)->FileNames[i]);
849 if (needReparse)
853 (*it)->reparse ();
855 catch (const EConfigFile &e)
857 nlwarning ("CF: Exception will re-read modified config file '%s': %s", (*it)->getFilename().c_str(), e.what ());
863 void CConfigFile::setTimeout (uint32 timeout)
865 _Timeout = timeout;
868 void CConfigFile::clear()
870 _Vars.clear ();
873 void CConfigFile::clearVars ()
875 for (vector<CVar>::iterator it = _Vars.begin (); it != _Vars.end (); it++)
877 (*it).Type = CVar::T_UNKNOWN;
881 uint CConfigFile::getNumVar () const
883 return (uint)_Vars.size ();
886 CConfigFile::CVar *CConfigFile::getVar (uint varId)
888 return &(_Vars[varId]);
891 CConfigFile::CVar *CConfigFile::insertVar (const std::string &varName, const CVar &varToCopy)
893 // Get the var
894 CVar *var = getVarPtr (varName);
895 if (!var)
897 _Vars.push_back (varToCopy);
898 var = &(_Vars.back ());
899 var->Root = false;
900 var->Name = varName;
902 return var;
905 } // NLMISC