$search
00001 // 00002 // CDataFile Class Implementation 00003 // 00004 // The purpose of this class is to provide a simple, full featured means to 00005 // store persistent data to a text file. It uses a simple key/value paradigm 00006 // to achieve this. The class can read/write to standard Windows .ini files, 00007 // and yet does not rely on any windows specific calls. It should work as 00008 // well in a linux environment (with some minor adjustments) as it does in 00009 // a Windows one. 00010 // 00011 // Written July, 2002 by Gary McNickle <gary#sunstorm.net> 00012 // If you use this class in your application, credit would be appreciated. 00013 // 00014 00015 // 00016 // CDataFile 00017 // The purpose of this class is to provide the means to easily store key/value 00018 // pairs in a config file, seperated by independant sections. Sections may not 00019 // have duplicate keys, although two or more sections can have the same key. 00020 // Simple support for comments is included. Each key, and each section may have 00021 // it's own multiline comment. 00022 // 00023 // An example might look like this; 00024 // 00025 // [UserSettings] 00026 // Name=Joe User 00027 // Date of Birth=12/25/01 00028 // 00029 // ; 00030 // ; Settings unique to this server 00031 // ; 00032 // [ServerSettings] 00033 // Port=1200 00034 // IP_Address=127.0.0.1 00035 // MachineName=ADMIN 00036 // 00037 00038 #include <cstdlib> 00039 #include <cstdio> 00040 #include <cstdarg> 00041 #include <cfloat> 00042 #include <cctype> 00043 #include <cstring> 00044 #include <climits> 00045 #include <vector> 00046 #include <string> 00047 #include <fstream> 00048 00049 #ifdef WIN32 00050 #include <windows.h> 00051 #endif 00052 00053 #include <blort/Tracker/CDataFile.h> 00054 00055 // Compatibility Defines //////////////////////////////////////////////////////// 00057 #ifdef WIN32 00058 #define snprintf _snprintf 00059 #define vsnprintf _vsnprintf 00060 #endif 00061 00062 using namespace std; 00063 00064 // CDataFile 00065 // Our default contstructor. If it can load the file, it will do so and populate 00066 // the section list with the values from the file. 00067 CDataFile::CDataFile(const t_Str& szFileName) 00068 { 00069 m_bDirty = false; 00070 m_szFileName = szFileName; 00071 m_Flags = (AUTOCREATE_SECTIONS | AUTOCREATE_KEYS); 00072 m_Sections.push_back( t_Section() ); 00073 00074 Load(m_szFileName); 00075 } 00076 00077 CDataFile::CDataFile() 00078 { 00079 Clear(); 00080 m_Flags = (AUTOCREATE_SECTIONS | AUTOCREATE_KEYS); 00081 m_Sections.push_back( t_Section() ); 00082 } 00083 00084 // ~CDataFile 00085 // Saves the file if any values have changed since the last save. 00086 CDataFile::~CDataFile() 00087 { 00088 //if ( m_bDirty ) // MZ: this does not seem to work! 00089 // Save(); 00090 } 00091 00092 // Clear 00093 // Resets the member variables to their defaults 00094 void CDataFile::Clear() 00095 { 00096 m_bDirty = false; 00097 m_szFileName = t_Str(""); 00098 m_Sections.clear(); 00099 } 00100 00101 // SetFileName 00102 // Set's the m_szFileName member variable. For use when creating the CDataFile 00103 // object by hand (-vs- loading it from a file 00104 void CDataFile::SetFileName(const t_Str& szFileName) 00105 { 00106 if (m_szFileName.size() != 0 && CompareNoCase(szFileName, m_szFileName) != 0) 00107 { 00108 m_bDirty = true; 00109 00110 Report(E_WARN, "[CDataFile::SetFileName] The filename has changed from <%s> to <%s>.", 00111 m_szFileName.c_str(), szFileName.c_str()); 00112 } 00113 00114 m_szFileName = szFileName; 00115 } 00116 00117 // Load 00118 // Attempts to load in the text file. If successful it will populate the 00119 // Section list with the key/value pairs found in the file. Note that comments 00120 // are saved so that they can be rewritten to the file later. 00121 bool CDataFile::Load(const t_Str& szFileName) 00122 { 00123 fstream File(szFileName.c_str(), ios::in); 00124 00125 if ( File.is_open() ) 00126 { 00127 bool bDone = false; 00128 bool bAutoKey = (m_Flags & AUTOCREATE_KEYS) == AUTOCREATE_KEYS; 00129 bool bAutoSec = (m_Flags & AUTOCREATE_SECTIONS) == AUTOCREATE_SECTIONS; 00130 00131 t_Str szLine; 00132 t_Str szComment; 00133 char buffer[MAX_BUFFER_LEN]; 00134 t_Section* pSection = GetSection(""); 00135 00136 // These need to be set, we'll restore the original values later. 00137 m_Flags |= AUTOCREATE_KEYS; 00138 m_Flags |= AUTOCREATE_SECTIONS; 00139 00140 while ( !bDone ) 00141 { 00142 memset(buffer, 0, MAX_BUFFER_LEN); 00143 File.getline(buffer, MAX_BUFFER_LEN); 00144 00145 szLine = buffer; 00146 Trim(szLine); 00147 00148 bDone = ( File.eof() || File.bad() || File.fail() ); 00149 00150 if ( szLine.find_first_of(CommentIndicators) == 0 ) 00151 { 00152 szComment += "\n"; 00153 szComment += szLine; 00154 } 00155 else 00156 if ( szLine.find_first_of('[') == 0 ) // new section 00157 { 00158 szLine.erase( 0, 1 ); 00159 szLine.erase( szLine.find_last_of(']'), 1 ); 00160 00161 CreateSection(szLine, szComment); 00162 pSection = GetSection(szLine); 00163 szComment = t_Str(""); 00164 } 00165 else 00166 if ( szLine.size() > 0 ) // we have a key, add this key/value pair 00167 { 00168 t_Str szKey = GetNextWord(szLine); 00169 t_Str szValue = szLine; 00170 00171 if ( szKey.size() > 0 && szValue.size() > 0 ) 00172 { 00173 SetValue(szKey, szValue, szComment, pSection->szName); 00174 szComment = t_Str(""); 00175 } 00176 } 00177 } 00178 00179 // Restore the original flag values. 00180 if ( !bAutoKey ) 00181 m_Flags &= ~AUTOCREATE_KEYS; 00182 00183 if ( !bAutoSec ) 00184 m_Flags &= ~AUTOCREATE_SECTIONS; 00185 } 00186 else 00187 { 00188 Report(E_INFO, "[CDataFile::Load] Unable to open file. Does it exist?"); 00189 return false; 00190 } 00191 00192 File.close(); 00193 00194 return true; 00195 } 00196 00197 00198 // Save 00199 // Attempts to save the Section list and keys to the file. Note that if Load 00200 // was never called (the CDataFile object was created manually), then you 00201 // must set the m_szFileName variable before calling save. 00202 bool CDataFile::Save() 00203 { 00204 if ( KeyCount() == 0 && SectionCount() == 0 ) 00205 { 00206 // no point in saving 00207 Report(E_INFO, "[CDataFile::Save] Nothing to save."); 00208 return false; 00209 } 00210 00211 if ( m_szFileName.size() == 0 ) 00212 { 00213 Report(E_ERROR, "[CDataFile::Save] No filename has been set."); 00214 return false; 00215 } 00216 00217 fstream File(m_szFileName.c_str(), ios::out|ios::trunc); 00218 00219 if ( File.is_open() ) 00220 { 00221 SectionItor s_pos; 00222 KeyItor k_pos; 00223 t_Section Section; 00224 t_Key Key; 00225 00226 for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); ++s_pos) 00227 { 00228 Section = (*s_pos); 00229 bool bWroteComment = false; 00230 00231 if ( Section.szComment.size() > 0 ) 00232 { 00233 bWroteComment = true; 00234 WriteLn(File, "\n%s", CommentStr(Section.szComment).c_str()); 00235 } 00236 00237 if ( Section.szName.size() > 0 ) 00238 { 00239 WriteLn(File, "%s[%s]", 00240 bWroteComment ? "" : "\n", 00241 Section.szName.c_str()); 00242 } 00243 00244 for (k_pos = Section.Keys.begin(); k_pos != Section.Keys.end(); ++k_pos) 00245 { 00246 Key = (*k_pos); 00247 00248 if ( Key.szKey.size() > 0 && Key.szValue.size() > 0 ) 00249 { 00250 WriteLn(File, "%s%s%s%s%c%s", 00251 Key.szComment.size() > 0 ? "\n" : "", 00252 CommentStr(Key.szComment).c_str(), 00253 Key.szComment.size() > 0 ? "\n" : "", 00254 Key.szKey.c_str(), 00255 EqualIndicators[0], 00256 Key.szValue.c_str()); 00257 } 00258 } 00259 } 00260 00261 } 00262 else 00263 { 00264 Report(E_ERROR, "[CDataFile::Save] Unable to save file."); 00265 return false; 00266 } 00267 00268 m_bDirty = false; 00269 00270 File.flush(); 00271 File.close(); 00272 00273 return true; 00274 } 00275 00276 // SetKeyComment 00277 // Set the comment of a given key. Returns true if the key is not found. 00278 bool CDataFile::SetKeyComment(const t_Str& szKey, const t_Str& szComment, 00279 const t_Str& szSection) 00280 { 00281 KeyItor k_pos; 00282 t_Section* pSection; 00283 00284 if ( (pSection = GetSection(szSection)) == NULL ) 00285 return false; 00286 00287 for (k_pos = pSection->Keys.begin(); k_pos != pSection->Keys.end(); ++k_pos) 00288 { 00289 if ( CompareNoCase( (*k_pos).szKey, szKey ) == 0 ) 00290 { 00291 (*k_pos).szComment = szComment; 00292 m_bDirty = true; 00293 return true; 00294 } 00295 } 00296 00297 return false; 00298 00299 } 00300 00301 // SetSectionComment 00302 // Set the comment for a given section. Returns false if the section 00303 // was not found. 00304 bool CDataFile::SetSectionComment(const t_Str& szSection, const t_Str& szComment) 00305 { 00306 SectionItor s_pos; 00307 00308 for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); ++s_pos) 00309 { 00310 if ( CompareNoCase( (*s_pos).szName, szSection ) == 0 ) 00311 { 00312 (*s_pos).szComment = szComment; 00313 m_bDirty = true; 00314 return true; 00315 } 00316 } 00317 00318 return false; 00319 } 00320 00321 00322 // SetValue 00323 // Given a key, a value and a section, this function will attempt to locate the 00324 // Key within the given section, and if it finds it, change the keys value to 00325 // the new value. If it does not locate the key, it will create a new key with 00326 // the proper value and place it in the section requested. 00327 bool CDataFile::SetValue(const t_Str& szKey, const t_Str& szValue, 00328 const t_Str& szComment, const t_Str& szSection) 00329 { 00330 t_Key* pKey = GetKey(szKey, szSection); 00331 t_Section* pSection = GetSection(szSection); 00332 00333 if (pSection == NULL) 00334 { 00335 if ( !(m_Flags & AUTOCREATE_SECTIONS) || !CreateSection(szSection,"")) 00336 return false; 00337 00338 pSection = GetSection(szSection); 00339 } 00340 00341 // Sanity check... 00342 if ( pSection == NULL ) 00343 return false; 00344 00345 // if the key does not exist in that section, and the value passed 00346 // is not t_Str("") then add the new key. 00347 if ( pKey == NULL && szValue.size() > 0 && (m_Flags & AUTOCREATE_KEYS)) 00348 { 00349 t_Key Key; 00350 00351 Key.szKey = szKey; 00352 Key.szValue = szValue; 00353 Key.szComment = szComment; 00354 00355 m_bDirty = true; 00356 00357 pSection->Keys.push_back(Key); 00358 00359 return true; 00360 } 00361 00362 if ( pKey != NULL ) 00363 { 00364 pKey->szValue = szValue; 00365 pKey->szComment = szComment; 00366 00367 m_bDirty = true; 00368 00369 return true; 00370 } 00371 00372 return false; 00373 } 00374 00375 // SetFloat 00376 // Passes the given float to SetValue as a string 00377 bool CDataFile::SetFloat(const t_Str& szKey, float fValue, 00378 const t_Str& szComment, const t_Str& szSection) 00379 { 00380 char szStr[64]; 00381 00382 snprintf(szStr, 64, "%f", fValue); 00383 00384 return SetValue(szKey, szStr, szComment, szSection); 00385 } 00386 00387 // SetInt 00388 // Passes the given int to SetValue as a string 00389 bool CDataFile::SetInt(const t_Str& szKey, int nValue, const t_Str& szComment, const t_Str& szSection) 00390 { 00391 char szStr[64]; 00392 00393 snprintf(szStr, 64, "%d", nValue); 00394 00395 return SetValue(szKey, szStr, szComment, szSection); 00396 00397 } 00398 00399 // SetBool 00400 // Passes the given bool to SetValue as a string 00401 bool CDataFile::SetBool(const t_Str& szKey, bool bValue, const t_Str& szComment, const t_Str& szSection) 00402 { 00403 t_Str szValue = bValue ? "True" : "False"; 00404 00405 return SetValue(szKey, szValue, szComment, szSection); 00406 } 00407 00408 // GetValue 00409 // Returns the key value as a t_Str object. A return value of 00410 // t_Str("") indicates that the key could not be found. 00411 t_Str CDataFile::GetValue(const t_Str& szKey, const t_Str& szSection) 00412 { 00413 t_Key* pKey = GetKey(szKey, szSection); 00414 00415 return (pKey == NULL) ? t_Str("") : pKey->szValue; 00416 } 00417 00418 // GetString 00419 // Returns the key value as a t_Str object. A return value of 00420 // t_Str("") indicates that the key could not be found. 00421 t_Str CDataFile::GetString(const t_Str& szKey, const t_Str& szSection) 00422 { 00423 return GetValue(szKey, szSection); 00424 } 00425 00426 // GetFloat 00427 // Returns the key value as a float type. Returns FLT_MIN if the key is 00428 // not found. 00429 float CDataFile::GetFloat(const t_Str& szKey, const t_Str& szSection) 00430 { 00431 t_Str szValue = GetValue(szKey, szSection); 00432 00433 if ( szValue.size() == 0 ) 00434 return FLT_MIN; 00435 return (float)atof( szValue.c_str() ); 00436 } 00437 00438 // GetInt 00439 // Returns the key value as an integer type. Returns INT_MIN if the key is 00440 // not found. 00441 int CDataFile::GetInt(const t_Str& szKey, const t_Str& szSection) 00442 { 00443 t_Str szValue = GetValue(szKey, szSection); 00444 00445 if ( szValue.size() == 0 ) 00446 return INT_MIN; 00447 00448 return atoi( szValue.c_str() ); 00449 } 00450 00451 // GetBool 00452 // Returns the key value as a bool type. Returns false if the key is 00453 // not found. 00454 bool CDataFile::GetBool(const t_Str& szKey, const t_Str& szSection) 00455 { 00456 bool bValue = false; 00457 t_Str szValue = GetValue(szKey, szSection); 00458 00459 if ( szValue.find("1") == 0 00460 || CompareNoCase(szValue, "true") == 0 00461 || CompareNoCase(szValue, "yes") == 0 ) 00462 { 00463 bValue = true; 00464 } 00465 00466 return bValue; 00467 } 00468 00469 // DeleteSection 00470 // Delete a specific section. Returns false if the section cannot be 00471 // found or true when sucessfully deleted. 00472 bool CDataFile::DeleteSection(const t_Str& szSection) 00473 { 00474 SectionItor s_pos; 00475 00476 for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); ++s_pos) 00477 { 00478 if ( CompareNoCase( (*s_pos).szName, szSection ) == 0 ) 00479 { 00480 m_Sections.erase(s_pos); 00481 return true; 00482 } 00483 } 00484 00485 return false; 00486 } 00487 00488 // DeleteKey 00489 // Delete a specific key in a specific section. Returns false if the key 00490 // cannot be found or true when sucessfully deleted. 00491 bool CDataFile::DeleteKey(const t_Str& szKey, const t_Str& szFromSection) 00492 { 00493 KeyItor k_pos; 00494 t_Section* pSection; 00495 00496 if ( (pSection = GetSection(szFromSection)) == NULL ) 00497 return false; 00498 00499 for (k_pos = pSection->Keys.begin(); k_pos != pSection->Keys.end(); ++k_pos) 00500 { 00501 if ( CompareNoCase( (*k_pos).szKey, szKey ) == 0 ) 00502 { 00503 pSection->Keys.erase(k_pos); 00504 return true; 00505 } 00506 } 00507 00508 return false; 00509 } 00510 00511 // CreateKey 00512 // Given a key, a value and a section, this function will attempt to locate the 00513 // Key within the given section, and if it finds it, change the keys value to 00514 // the new value. If it does not locate the key, it will create a new key with 00515 // the proper value and place it in the section requested. 00516 bool CDataFile::CreateKey(const t_Str& szKey, const t_Str& szValue, 00517 const t_Str& szComment, const t_Str& szSection) 00518 { 00519 bool bAutoKey = (m_Flags & AUTOCREATE_KEYS) == AUTOCREATE_KEYS; 00520 bool bReturn = false; 00521 00522 m_Flags |= AUTOCREATE_KEYS; 00523 00524 bReturn = SetValue(szKey, szValue, szComment, szSection); 00525 00526 if ( !bAutoKey ) 00527 m_Flags &= ~AUTOCREATE_KEYS; 00528 00529 return bReturn; 00530 } 00531 00532 00533 // CreateSection 00534 // Given a section name, this function first checks to see if the given section 00535 // allready exists in the list or not, if not, it creates the new section and 00536 // assigns it the comment given in szComment. The function returns true if 00537 // sucessfully created, or false otherwise. 00538 bool CDataFile::CreateSection(const t_Str& szSection, const t_Str& szComment) 00539 { 00540 t_Section* pSection = GetSection(szSection); 00541 00542 if ( pSection ) 00543 { 00544 Report(E_INFO, "[CDataFile::CreateSection] Section <%s> allready exists. Aborting.", szSection.c_str()); 00545 return false; 00546 } 00547 00548 t_Section Section; 00549 00550 Section.szName = szSection; 00551 Section.szComment = szComment; 00552 m_Sections.push_back(Section); 00553 m_bDirty = true; 00554 00555 return true; 00556 } 00557 00558 // CreateSection 00559 // Given a section name, this function first checks to see if the given section 00560 // allready exists in the list or not, if not, it creates the new section and 00561 // assigns it the comment given in szComment. The function returns true if 00562 // sucessfully created, or false otherwise. This version accpets a KeyList 00563 // and sets up the newly created Section with the keys in the list. 00564 bool CDataFile::CreateSection(const t_Str& szSection, const t_Str& szComment, 00565 const KeyList& Keys) 00566 { 00567 if ( !CreateSection(szSection, szComment) ) 00568 return false; 00569 00570 t_Section* pSection = GetSection(szSection); 00571 00572 if ( !pSection ) 00573 return false; 00574 00575 pSection->Keys = Keys; 00576 00577 m_bDirty = true; 00578 00579 return true; 00580 } 00581 00582 // SectionCount 00583 // Simply returns the number of sections in the list. 00584 int CDataFile::SectionCount() 00585 { 00586 return m_Sections.size(); 00587 } 00588 00589 // KeyCount 00590 // Returns the total number of keys contained within all the sections. 00591 int CDataFile::KeyCount() 00592 { 00593 int nCounter = 0; 00594 SectionItor s_pos; 00595 00596 for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); ++s_pos) 00597 nCounter += (*s_pos).Keys.size(); 00598 00599 return nCounter; 00600 } 00601 00602 00603 // Protected Member Functions /////////////////////////////////////////////////// 00605 00606 // GetKey 00607 // Given a key and section name, looks up the key and if found, returns a 00608 // pointer to that key, otherwise returns NULL. 00609 t_Key* CDataFile::GetKey(const t_Str& szKey, const t_Str& szSection) 00610 { 00611 KeyItor k_pos; 00612 t_Section* pSection; 00613 00614 // Since our default section has a name value of t_Str("") this should 00615 // always return a valid section, wether or not it has any keys in it is 00616 // another matter. 00617 if ( (pSection = GetSection(szSection)) == NULL ) 00618 return NULL; 00619 00620 for (k_pos = pSection->Keys.begin(); k_pos != pSection->Keys.end(); ++k_pos) 00621 { 00622 if ( CompareNoCase( (*k_pos).szKey, szKey ) == 0 ) 00623 return (t_Key*)&(*k_pos); 00624 } 00625 00626 return NULL; 00627 } 00628 00629 // GetSection 00630 // Given a section name, locates that section in the list and returns a pointer 00631 // to it. If the section was not found, returns NULL 00632 t_Section* CDataFile::GetSection(const t_Str& szSection) 00633 { 00634 SectionItor s_pos; 00635 00636 for (s_pos = m_Sections.begin(); s_pos != m_Sections.end(); ++s_pos) 00637 { 00638 if ( CompareNoCase( (*s_pos).szName, szSection ) == 0 ) 00639 return (t_Section*)&(*s_pos); 00640 } 00641 00642 return NULL; 00643 } 00644 00645 00646 t_Str CDataFile::CommentStr(const t_Str& szComment) 00647 { 00648 t_Str szNewStr = szComment; 00649 00650 Trim(szNewStr); 00651 00652 if ( szNewStr.size() == 0 ) 00653 return szNewStr; 00654 00655 if ( szNewStr.find_first_of(CommentIndicators) != 0 ) 00656 { 00657 t_Str szPreStr; 00658 szPreStr += CommentIndicators[0]; 00659 szPreStr += " "; 00660 szNewStr.insert(0, szPreStr); 00661 } 00662 00663 return szNewStr; 00664 } 00665 00666 00667 00668 // Utility Functions //////////////////////////////////////////////////////////// 00670 00671 // GetNextWord 00672 // Given a key +delimiter+ value string, pulls the key name from the string, 00673 // deletes the delimiter and alters the original string to contain the 00674 // remainder. Returns the key 00675 t_Str GetNextWord(t_Str& CommandLine) 00676 { 00677 unsigned nPos = CommandLine.find_first_of(EqualIndicators); 00678 t_Str sWord = t_Str(""); 00679 00680 if ( nPos != string::npos ) 00681 { 00682 sWord = CommandLine.substr(0, nPos); 00683 CommandLine.erase(0, nPos+1); 00684 } 00685 else 00686 { 00687 sWord = CommandLine; 00688 CommandLine = t_Str(""); 00689 } 00690 00691 Trim(CommandLine); 00692 Trim(sWord); 00693 return sWord; 00694 } 00695 00696 00697 // CompareNoCase 00698 // it's amazing what features std::string lacks. This function simply 00699 // does a lowercase compare against the two strings, returning 0 if they 00700 // match. 00701 int CompareNoCase(const t_Str& str1, const t_Str& str2) 00702 { 00703 #ifdef WIN32 00704 return _stricmp(str1.c_str(), str2.c_str()); 00705 #else 00706 return strcasecmp(str1.c_str(), str2.c_str()); 00707 #endif 00708 } 00709 00710 // Trim 00711 // Trims whitespace from both sides of a string. 00712 void Trim(t_Str& szStr) 00713 { 00714 t_Str szTrimChars = WhiteSpace; 00715 szTrimChars += EqualIndicators; 00716 00717 // trim left 00718 size_t nPos = szStr.find_first_not_of(szTrimChars); 00719 if(nPos != string::npos) 00720 { 00721 szStr.erase(0, nPos); 00722 // trim right 00723 size_t rPos = szStr.find_last_not_of(szTrimChars); 00724 if (rPos != string::npos && rPos < szStr.size() - 1) 00725 szStr.erase(rPos + 1); 00726 } 00727 else // else the string is all white space 00728 { 00729 szStr.erase(); 00730 } 00731 } 00732 00733 // WriteLn 00734 // Writes the formatted output to the file stream, returning the number of 00735 // bytes written. 00736 int WriteLn(fstream& stream, const char* fmt, ...) 00737 { 00738 char buf[MAX_BUFFER_LEN]; 00739 int nLength; 00740 00741 memset(buf, 0, MAX_BUFFER_LEN); 00742 va_list args; 00743 00744 va_start (args, fmt); 00745 nLength = vsnprintf(buf, MAX_BUFFER_LEN, fmt, args); 00746 va_end (args); 00747 00748 00749 if ( buf[nLength] != '\n' && buf[nLength] != '\r' ) 00750 buf[nLength++] = '\n'; 00751 00752 00753 stream.write(buf, nLength); 00754 00755 return nLength; 00756 } 00757 00758 // Report 00759 // A simple reporting function. Outputs the report messages to stdout 00760 // This is a dumb'd down version of a simmilar function of mine, so if 00761 // it looks like it should do more than it does, that's why... 00762 void Report(e_DebugLevel DebugLevel, const char *fmt, ...) 00763 { 00764 char buf[MAX_BUFFER_LEN]; 00765 int nLength; 00766 t_Str szMsg; 00767 00768 va_list args; 00769 00770 memset(buf, 0, MAX_BUFFER_LEN); 00771 00772 va_start (args, fmt); 00773 nLength = vsnprintf(buf, MAX_BUFFER_LEN, fmt, args); 00774 va_end (args); 00775 00776 00777 if ( buf[nLength] != '\n' && buf[nLength] != '\r' ) 00778 buf[nLength++] = '\n'; 00779 00780 00781 switch ( DebugLevel ) 00782 { 00783 case E_DEBUG: 00784 szMsg = "<debug> "; 00785 break; 00786 case E_INFO: 00787 szMsg = "<info> "; 00788 break; 00789 case E_WARN: 00790 szMsg = "<warn> "; 00791 break; 00792 case E_ERROR: 00793 szMsg = "<error> "; 00794 break; 00795 case E_FATAL: 00796 szMsg = "<fatal> "; 00797 break; 00798 case E_CRITICAL: 00799 szMsg = "<critical> "; 00800 break; 00801 } 00802 00803 00804 szMsg += buf; 00805 00806 00807 #ifdef WIN32 00808 OutputDebugString(szMsg.c_str()); 00809 #endif 00810 00811 printf("%s", szMsg.c_str()); 00812 00813 } 00814