1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Assorted wxLogXXX functions, and wxLog (sink for logs) 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows license 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20   #pragma implementation "log.h" 
  23 // For compilers that support precompilation, includes "wx.h". 
  24 #include "wx/wxprec.h" 
  34   #include  <wx/string.h> 
  38   #include  <wx/msgdlg.h> 
  39   #include  <wx/filedlg.h> 
  40   #include  <wx/textctrl.h> 
  44 #include  <wx/textfile.h> 
  48 // other standard headers 
  55   // Redefines OutputDebugString if necessary 
  56   #include  "wx/msw/private.h" 
  61 // ---------------------------------------------------------------------------- 
  62 // non member functions 
  63 // ---------------------------------------------------------------------------- 
  65 // define this to enable wrapping of log messages 
  66 //#define LOG_PRETTY_WRAP 
  68 #ifdef  LOG_PRETTY_WRAP 
  69   static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
); 
  72 // ---------------------------------------------------------------------------- 
  74 // ---------------------------------------------------------------------------- 
  76 // we use a global variable to store the frame pointer for wxLogStatus - bad, 
  77 // but it's he easiest way 
  78 static wxFrame 
*gs_pFrame
; 
  80 // ============================================================================ 
  82 // ============================================================================ 
  84 // ---------------------------------------------------------------------------- 
  85 // implementation of Log functions 
  87 // NB: unfortunately we need all these distinct functions, we can't make them 
  88 //     macros and not all compilers inline vararg functions. 
  89 // ---------------------------------------------------------------------------- 
  91 // log functions can't allocate memory (LogError("out of memory...") should 
  92 // work!), so we use a static buffer for all log messages 
  93 #define LOG_BUFFER_SIZE   (4096) 
  95 // static buffer for error messages (@@@ MT-unsafe) 
  96 static char s_szBuf
[LOG_BUFFER_SIZE
]; 
  98 // generic log function 
  99 void wxLogGeneric(wxLogLevel level
, const char *szFormat
, ...) 
 101   if ( wxLog::GetActiveTarget() != NULL 
) { 
 103     va_start(argptr
, szFormat
); 
 104     vsprintf(s_szBuf
, szFormat
, argptr
); 
 107     wxLog::OnLog(level
, s_szBuf
); 
 111 #define IMPLEMENT_LOG_FUNCTION(level)                             \ 
 112   void wxLog##level(const char *szFormat, ...)                    \ 
 114     if ( wxLog::GetActiveTarget() != NULL ) {                     \ 
 116       va_start(argptr, szFormat);                                 \ 
 117       vsprintf(s_szBuf, szFormat, argptr);                        \ 
 120       wxLog::OnLog(wxLOG_##level, s_szBuf);                       \ 
 124 IMPLEMENT_LOG_FUNCTION(FatalError
) 
 125 IMPLEMENT_LOG_FUNCTION(Error
) 
 126 IMPLEMENT_LOG_FUNCTION(Warning
) 
 127 IMPLEMENT_LOG_FUNCTION(Message
) 
 128 IMPLEMENT_LOG_FUNCTION(Info
) 
 129 IMPLEMENT_LOG_FUNCTION(Status
) 
 131 // accepts an additional argument which tells to which frame the output should 
 133 void wxLogStatus(wxFrame 
*pFrame
, const char *szFormat
, ...) 
 135   wxLog 
*pLog 
= wxLog::GetActiveTarget(); 
 136   if ( pLog 
!= NULL 
) { 
 138     va_start(argptr
, szFormat
); 
 139     vsprintf(s_szBuf
, szFormat
, argptr
); 
 142     wxASSERT( gs_pFrame 
== NULL 
); // should be reset! 
 144     wxLog::OnLog(wxLOG_Status
, s_szBuf
); 
 145     gs_pFrame 
= (wxFrame 
*) NULL
; 
 149 // same as info, but only if 'verbose' mode is on 
 150 void wxLogVerbose(const char *szFormat
, ...) 
 152   wxLog 
*pLog 
= wxLog::GetActiveTarget(); 
 153   if ( pLog 
!= NULL 
&& pLog
->GetVerbose() ) { 
 155     va_start(argptr
, szFormat
); 
 156     vsprintf(s_szBuf
, szFormat
, argptr
); 
 159     wxLog::OnLog(wxLOG_Info
, s_szBuf
); 
 165 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)                       \ 
 166   void wxLog##level(const char *szFormat, ...)                    \ 
 168     if ( wxLog::GetActiveTarget() != NULL ) {                     \ 
 170       va_start(argptr, szFormat);                                 \ 
 171       vsprintf(s_szBuf, szFormat, argptr);                        \ 
 174       wxLog::OnLog(wxLOG_##level, s_szBuf);                       \ 
 178   void wxLogTrace(wxTraceMask mask
, const char *szFormat
, ...) 
 180     wxLog 
*pLog 
= wxLog::GetActiveTarget(); 
 182     // we check that all of mask bits are set in the current mask, so 
 183     // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something 
 184     // if both bits are set. 
 185     if ( pLog 
!= NULL 
&& ((pLog
->GetTraceMask() & mask
) == mask
) ) { 
 187       va_start(argptr
, szFormat
); 
 188       vsprintf(s_szBuf
, szFormat
, argptr
); 
 191       wxLog::OnLog(wxLOG_Trace
, s_szBuf
); 
 196   #define IMPLEMENT_LOG_DEBUG_FUNCTION(level) 
 199 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug
) 
 200 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace
) 
 202 // wxLogSysError: one uses the last error code, for other  you must give it 
 205 // common part of both wxLogSysError 
 206 void wxLogSysErrorHelper(long lErrCode
) 
 208   char szErrMsg
[LOG_BUFFER_SIZE 
/ 2]; 
 209   sprintf(szErrMsg
, _(" (error %ld: %s)"), lErrCode
, wxSysErrorMsg(lErrCode
)); 
 210   strncat(s_szBuf
, szErrMsg
, WXSIZEOF(s_szBuf
) - strlen(s_szBuf
)); 
 212   wxLog::OnLog(wxLOG_Error
, s_szBuf
); 
 215 void WXDLLEXPORT 
wxLogSysError(const char *szFormat
, ...) 
 218   va_start(argptr
, szFormat
); 
 219   vsprintf(s_szBuf
, szFormat
, argptr
); 
 222   wxLogSysErrorHelper(wxSysErrorCode()); 
 225 void WXDLLEXPORT 
wxLogSysError(long lErrCode
, const char *szFormat
, ...) 
 228   va_start(argptr
, szFormat
); 
 229   vsprintf(s_szBuf
, szFormat
, argptr
); 
 232   wxLogSysErrorHelper(lErrCode
); 
 235 // ---------------------------------------------------------------------------- 
 236 // wxLog class implementation 
 237 // ---------------------------------------------------------------------------- 
 241   m_bHasMessages 
= FALSE
; 
 243   // enable verbose messages by default in the debug builds 
 248 #endif // debug/release 
 250   m_szTimeFormat 
= "[%d/%b/%y %H:%M:%S] "; 
 253 wxLog 
*wxLog::GetActiveTarget() 
 255   if ( ms_bAutoCreate 
&& ms_pLogger 
== NULL 
) { 
 256     // prevent infinite recursion if someone calls wxLogXXX() from 
 257     // wxApp::CreateLogTarget() 
 258     static bool s_bInGetActiveTarget 
= FALSE
; 
 259     if ( !s_bInGetActiveTarget 
) { 
 260       s_bInGetActiveTarget 
= TRUE
; 
 263         ms_pLogger 
= new wxLogStderr
; 
 265         // ask the application to create a log target for us 
 266         if ( wxTheApp 
!= NULL 
) 
 267           ms_pLogger 
= wxTheApp
->CreateLogTarget(); 
 269           ms_pLogger 
= new wxLogStderr
; 
 272       s_bInGetActiveTarget 
= FALSE
; 
 274       // do nothing if it fails - what can we do? 
 281 wxLog 
*wxLog::SetActiveTarget(wxLog 
*pLogger
) 
 283   if ( ms_pLogger 
!= NULL 
) { 
 284     // flush the old messages before changing because otherwise they might 
 285     // get lost later if this target is not restored 
 289   wxLog 
*pOldLogger 
= ms_pLogger
; 
 290   ms_pLogger 
= pLogger
; 
 295 wxString 
wxLog::TimeStamp() const 
 299 /* Let's disable TimeStamp and see if anyone complains. 
 300  * If not, we'll remove it, since it's probably unlikely 
 301  * to ever be used. -- JACS 22/11/98 
 302   if ( !IsEmpty(m_szTimeFormat) ) { 
 308     ptmNow = localtime(&timeNow); 
 310     strftime(szBuf, WXSIZEOF(szBuf), m_szTimeFormat, ptmNow); 
 318 void wxLog::DoLog(wxLogLevel level
, const char *szString
) 
 320   // prepend a timestamp if not disabled 
 321   wxString str 
= TimeStamp(); 
 324     case wxLOG_FatalError
: 
 325       DoLogString(str 
<< _("Fatal error: ") << szString
); 
 326       DoLogString(_("Program aborted.")); 
 332       DoLogString(str 
<< _("Error: ") << szString
); 
 336       DoLogString(str 
<< _("Warning: ") << szString
); 
 342         DoLogString(str 
+ szString
); 
 352       //        DoLogString(str << (level == wxLOG_Trace ? _("Trace") : _("Debug")) 
 353       //                        << ": " << szString); 
 354       // JACS: we don't really want to prefix with 'Debug'. It's just extra 
 356               DoLogString(szString
); 
 362       wxFAIL_MSG(_("unknown log level in wxLog::DoLog")); 
 366 void wxLog::DoLogString(const char *WXUNUSED(szString
)) 
 368   wxFAIL_MSG(_("DoLogString must be overrided if it's called.")); 
 376 // ---------------------------------------------------------------------------- 
 377 // wxLogStderr class implementation 
 378 // ---------------------------------------------------------------------------- 
 380 wxLogStderr::wxLogStderr(FILE *fp
) 
 388 void wxLogStderr::DoLogString(const char *szString
) 
 390   fputs(szString
, m_fp
); 
 395 // ---------------------------------------------------------------------------- 
 396 // wxLogStream implementation 
 397 // ---------------------------------------------------------------------------- 
 399 #if wxUSE_STD_IOSTREAM 
 400 wxLogStream::wxLogStream(ostream 
*ostr
) 
 408 void wxLogStream::DoLogString(const char *szString
) 
 410   (*m_ostr
) << szString 
<< endl 
<< flush
; 
 416 // ---------------------------------------------------------------------------- 
 417 // wxLogTextCtrl implementation 
 418 // ---------------------------------------------------------------------------- 
 420 #if wxUSE_STD_IOSTREAM 
 421 wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl 
*pTextCtrl
) 
 422 // DLL mode in wxMSW, can't use it. 
 423 #if defined(NO_TEXT_WINDOW_STREAM) 
 425              : wxLogStream(new ostream(pTextCtrl
)) 
 430 wxLogTextCtrl::~wxLogTextCtrl() 
 436 // ---------------------------------------------------------------------------- 
 437 // wxLogGui implementation 
 438 // ---------------------------------------------------------------------------- 
 445 void wxLogGui::Flush() 
 447   if ( !m_bHasMessages 
) 
 450   // do it right now to block any new calls to Flush() while we're here 
 451   m_bHasMessages 
= FALSE
; 
 455   // concatenate all strings (but not too many to not overfill the msg box) 
 458        nMsgCount 
= m_aMessages
.Count(); 
 460   // start from the most recent message 
 461   for ( size_t n 
= nMsgCount
; n 
> 0; n
-- ) { 
 462     // for Windows strings longer than this value are wrapped (NT 4.0) 
 463     const size_t nMsgLineWidth 
= 156; 
 465     nLines 
+= (m_aMessages
[n 
- 1].Len() + nMsgLineWidth 
- 1) / nMsgLineWidth
; 
 467     if ( nLines 
> 25 )  // don't put too many lines in message box 
 470     str 
<< m_aMessages
[n 
- 1] << "\n"; 
 474     wxMessageBox(str
, _("Error"), wxOK 
| wxICON_EXCLAMATION
); 
 477     wxMessageBox(str
, _("Information"), wxOK 
| wxICON_INFORMATION
); 
 480   // no undisplayed messages whatsoever 
 485 // the default behaviour is to discard all informational messages if there 
 486 // are any errors/warnings. 
 487 void wxLogGui::DoLog(wxLogLevel level
, const char *szString
) 
 494           m_aMessages
.Add(szString
); 
 495           m_bHasMessages 
= TRUE
; 
 501         // find the top window and set it's status text if it has any 
 502         wxFrame 
*pFrame 
= gs_pFrame
; 
 503         if ( pFrame 
== NULL 
) { 
 504           wxWindow 
*pWin 
= wxTheApp
->GetTopWindow(); 
 505           if ( pWin 
!= NULL 
&& pWin
->IsKindOf(CLASSINFO(wxFrame
)) ) { 
 506             pFrame 
= (wxFrame 
*)pWin
; 
 510         if ( pFrame 
!= NULL 
) 
 511           pFrame
->SetStatusText(szString
); 
 519           wxString strTime 
= TimeStamp(); 
 522               // don't prepend debug/trace here: it goes to the debug window 
 523               // anyhow, but do put a timestamp 
 524               OutputDebugString(strTime 
+ szString 
+ "\n\r"); 
 526             // send them to stderr 
 528             fprintf(stderr, "%s %s: %s\n", 
 530                     level == wxLOG_Trace ? _("Trace") : _("Debug"), 
 533             fprintf(stderr
, "%s\n", 
 541     case wxLOG_FatalError
: 
 542       // show this one immediately 
 543       wxMessageBox(szString
, _("Fatal error"), wxICON_HAND
); 
 548       // discard earlier informational messages if this is the 1st error 
 551         m_bHasMessages 
= TRUE
; 
 555       m_aMessages
.Add(szString
); 
 559       wxFAIL_MSG(_("unknown log level in wxLogGui::DoLog")); 
 563 // ---------------------------------------------------------------------------- 
 564 // wxLogWindow and wxLogFrame implementation 
 565 // ---------------------------------------------------------------------------- 
 569 class wxLogFrame 
: public wxFrame
 
 573   wxLogFrame(wxFrame 
*pParent
, wxLogWindow 
*log
, const char *szTitle
); 
 574   virtual ~wxLogFrame(); 
 577   void OnClose(wxCommandEvent
& event
); 
 578   void OnCloseWindow(wxCloseEvent
& event
); 
 579   void OnSave (wxCommandEvent
& event
); 
 580   void OnClear(wxCommandEvent
& event
); 
 582   void OnIdle(wxIdleEvent
&); 
 585   wxTextCtrl 
*TextCtrl() const { return m_pTextCtrl
; } 
 595   // instead of closing just hide the window to be able to Show() it later 
 596   void DoClose() { Show(FALSE
); } 
 598   wxTextCtrl  
*m_pTextCtrl
; 
 601   DECLARE_EVENT_TABLE() 
 604 BEGIN_EVENT_TABLE(wxLogFrame
, wxFrame
) 
 605   // wxLogWindow menu events 
 606   EVT_MENU(Menu_Close
, wxLogFrame::OnClose
) 
 607   EVT_MENU(Menu_Save
,  wxLogFrame::OnSave
) 
 608   EVT_MENU(Menu_Clear
, wxLogFrame::OnClear
) 
 610   EVT_CLOSE(wxLogFrame::OnCloseWindow
) 
 613 wxLogFrame::wxLogFrame(wxFrame 
*pParent
, wxLogWindow 
*log
, const char *szTitle
) 
 614           : wxFrame(pParent
, -1, szTitle
) 
 618   // @@ kludge: wxSIMPLE_BORDER is simply to prevent wxWindows from creating 
 619   //    a rich edit control instead of a normal one we want in wxMSW 
 620   m_pTextCtrl 
= new wxTextCtrl(this, -1, wxEmptyString
, wxDefaultPosition
, 
 628   wxMenuBar 
*pMenuBar 
= new wxMenuBar
; 
 629   wxMenu 
*pMenu 
= new wxMenu
; 
 630   pMenu
->Append(Menu_Save
,  _("&Save..."), _("Save log contents to file")); 
 631   pMenu
->Append(Menu_Clear
, _("C&lear"), _("Clear the log contents")); 
 632   pMenu
->AppendSeparator(); 
 633   pMenu
->Append(Menu_Close
, _("&Close"), _("Close this window")); 
 634   pMenuBar
->Append(pMenu
, _("&Log")); 
 635   SetMenuBar(pMenuBar
); 
 637   // status bar for menu prompts 
 640   m_log
->OnFrameCreate(this); 
 643 void wxLogFrame::OnClose(wxCommandEvent
& WXUNUSED(event
)) 
 648 void wxLogFrame::OnCloseWindow(wxCloseEvent
& WXUNUSED(event
)) 
 653 void wxLogFrame::OnSave(wxCommandEvent
& WXUNUSED(event
)) 
 657   const char *szFileName 
= wxSaveFileSelector("log", "txt", "log.txt"); 
 658   if ( szFileName 
== NULL 
) { 
 667   if ( wxFile::Exists(szFileName
) ) { 
 668     bool bAppend 
= FALSE
; 
 670     strMsg
.Printf(_("Append log to file '%s' " 
 671                     "(choosing [No] will overwrite it)?"), szFileName
); 
 672     switch ( wxMessageBox(strMsg
, _("Question"), wxYES_NO 
| wxCANCEL
) ) { 
 685         wxFAIL_MSG(_("invalid message box return value")); 
 689       bOk 
= file
.Open(szFileName
, wxFile::write_append
); 
 692       bOk 
= file
.Create(szFileName
, TRUE 
/* overwrite */); 
 696     bOk 
= file
.Create(szFileName
); 
 699   // retrieve text and save it 
 700   // ------------------------- 
 701   int nLines 
= m_pTextCtrl
->GetNumberOfLines(); 
 702   for ( int nLine 
= 0; bOk 
&& nLine 
< nLines
; nLine
++ ) { 
 703     bOk 
= file
.Write(m_pTextCtrl
->GetLineText(nLine
) +  
 704 // we're not going to pull in the whole wxTextFile if all we need is this... 
 707 #else // !wxUSE_TEXTFILE 
 709 #endif // wxUSE_TEXTFILE 
 717     wxLogError(_("Can't save log contents to file.")); 
 720     wxLogStatus(this, _("Log saved to the file '%s'."), szFileName
); 
 724 void wxLogFrame::OnClear(wxCommandEvent
& WXUNUSED(event
)) 
 726   m_pTextCtrl
->Clear(); 
 729 wxLogFrame::~wxLogFrame() 
 731   m_log
->OnFrameDelete(this); 
 736 wxLogWindow::wxLogWindow(wxFrame 
*pParent
, 
 741   m_bPassMessages 
= bDoPass
; 
 743   m_pLogFrame 
= new wxLogFrame(pParent
, this, szTitle
); 
 744   m_pOldLog 
= wxLog::SetActiveTarget(this); 
 747     m_pLogFrame
->Show(TRUE
); 
 750 void wxLogWindow::Show(bool bShow
) 
 752   m_pLogFrame
->Show(bShow
); 
 755 void wxLogWindow::Flush() 
 757   if ( m_pOldLog 
!= NULL 
) 
 760   m_bHasMessages 
= FALSE
; 
 763 void wxLogWindow::DoLog(wxLogLevel level
, const char *szString
) 
 765   // first let the previous logger show it 
 766   if ( m_pOldLog 
!= NULL 
&& m_bPassMessages 
) { 
 767     // @@@ why can't we access protected wxLog method from here (we derive 
 768     // from wxLog)? gcc gives "DoLog is protected in this context", what 
 769     // does this mean? Anyhow, the cast is harmless and let's us do what 
 771     ((wxLogWindow 
*)m_pOldLog
)->DoLog(level
, szString
); 
 777         // by default, these messages are ignored by wxLog, so process 
 780           wxString str 
= TimeStamp(); 
 781           str 
<< _("Status: ") << szString
; 
 786       // don't put trace messages in the text window for 2 reasons: 
 787       // 1) there are too many of them 
 788       // 2) they may provoke other trace messages thus sending a program 
 789       //    into an infinite loop 
 794         // and this will format it nicely and call our DoLogString() 
 795         wxLog::DoLog(level
, szString
); 
 799   m_bHasMessages 
= TRUE
; 
 802 void wxLogWindow::DoLogString(const char *szString
) 
 804   // put the text into our window 
 805   wxTextCtrl 
*pText 
= m_pLogFrame
->TextCtrl(); 
 807   // remove selection (WriteText is in fact ReplaceSelection) 
 809     long nLen 
= pText
->GetLastPosition(); 
 810     pText
->SetSelection(nLen
, nLen
); 
 813   pText
->WriteText(szString
); 
 814   pText
->WriteText("\n"); // "\n" ok here (_not_ "\r\n") 
 816   // TODO ensure that the line can be seen 
 819 wxFrame 
*wxLogWindow::GetFrame() const 
 824 void wxLogWindow::OnFrameCreate(wxFrame 
* WXUNUSED(frame
)) 
 828 void wxLogWindow::OnFrameDelete(wxFrame 
* WXUNUSED(frame
)) 
 830   m_pLogFrame 
= (wxLogFrame 
*)NULL
; 
 833 wxLogWindow::~wxLogWindow() 
 837   // may be NULL if log frame already auto destroyed itself 
 843 // ============================================================================ 
 844 // Global functions/variables 
 845 // ============================================================================ 
 847 // ---------------------------------------------------------------------------- 
 849 // ---------------------------------------------------------------------------- 
 850 wxLog      
*wxLog::ms_pLogger      
= (wxLog 
*) NULL
; 
 851 bool        wxLog::ms_doLog        
= TRUE
; 
 852 bool        wxLog::ms_bAutoCreate  
= TRUE
; 
 853 wxTraceMask 
wxLog::ms_ulTraceMask  
= (wxTraceMask
)0; 
 855 // ---------------------------------------------------------------------------- 
 856 // stdout error logging helper 
 857 // ---------------------------------------------------------------------------- 
 859 // helper function: wraps the message and justifies it under given position 
 860 // (looks more pretty on the terminal). Also adds newline at the end. 
 862 // @@ this is now disabled until I find a portable way of determining the 
 863 //    terminal window size (ok, I found it but does anybody really cares?) 
 864 #ifdef    LOG_PRETTY_WRAP 
 865 static void wxLogWrap(FILE *f
, const char *pszPrefix
, const char *psz
) 
 867   size_t nMax 
= 80; // @@@@ 
 868   size_t nStart 
= strlen(pszPrefix
); 
 872   while ( *psz 
!= '\0' ) { 
 873     for ( n 
= nStart
; (n 
< nMax
) && (*psz 
!= '\0'); n
++ ) 
 877     if ( *psz 
!= '\0' ) { 
 879       for ( n 
= 0; n 
< nStart
; n
++ ) 
 882       // as we wrapped, squeeze all white space 
 883       while ( isspace(*psz
) ) 
 890 #endif  //LOG_PRETTY_WRAP 
 892 // ---------------------------------------------------------------------------- 
 893 // error code/error message retrieval functions 
 894 // ---------------------------------------------------------------------------- 
 896 // get error code from syste 
 897 unsigned long wxSysErrorCode() 
 901       return ::GetLastError(); 
 903       // @@@@ what to do on Windows 3.1? 
 911 // get error message from system 
 912 const char *wxSysErrorMsg(unsigned long nErrCode
) 
 915     nErrCode 
= wxSysErrorCode(); 
 919       static char s_szBuf
[LOG_BUFFER_SIZE 
/ 2]; 
 921       // get error message from system 
 923       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER 
| FORMAT_MESSAGE_FROM_SYSTEM
, 
 925                     MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
), 
 929       // copy it to our buffer and free memory 
 930       strncpy(s_szBuf
, (const char *)lpMsgBuf
, WXSIZEOF(s_szBuf
) - 1); 
 931       s_szBuf
[WXSIZEOF(s_szBuf
) - 1] = '\0'; 
 934       // returned string is capitalized and ended with '\r\n' - bad 
 935       s_szBuf
[0] = (char)wxToLower(s_szBuf
[0]); 
 936       size_t len 
= strlen(s_szBuf
); 
 939         if ( s_szBuf
[len 
- 2] == '\r' ) 
 940           s_szBuf
[len 
- 2] = '\0'; 
 949     return strerror(nErrCode
); 
 953 // ---------------------------------------------------------------------------- 
 955 // ---------------------------------------------------------------------------- 
 963   #elif defined(__WXSTUBS__) 
 965   #elif defined(__WXMAC__) 
 976 // this function is called when an assert fails 
 977 void wxOnAssert(const char *szFile
, int nLine
, const char *szMsg
) 
 979   // this variable can be set to true to suppress "assert failure" messages 
 980   static bool s_bNoAsserts 
= FALSE
; 
 981   static bool s_bInAssert 
= FALSE
; 
 984     // He-e-e-e-elp!! we're trapped in endless loop 
 992   char szBuf
[LOG_BUFFER_SIZE
]; 
 994   // make life easier for people using VC++ IDE: clicking on the message will 
 995   // take us immediately to the place of the failed assert 
 997   sprintf(szBuf
, "%s(%d): assert failed", szFile
, nLine
); 
 999   // make the error message more clear for all the others 
1000   sprintf(szBuf
, "Assert failed in file %s at line %d", szFile
, nLine
); 
1003   if ( szMsg 
!= NULL 
) { 
1004     strcat(szBuf
, ": "); 
1005     strcat(szBuf
, szMsg
); 
1011   if ( !s_bNoAsserts 
) { 
1012     // send it to the normal log destination 
1018       strcat(szBuf
, "\nDo you want to stop the program?" 
1019                     "\nYou can also choose [Cancel] to suppress " 
1020                     "further warnings."); 
1022       switch ( wxMessageBox(szBuf
, _("Debug"), 
1023                             wxYES_NO 
| wxCANCEL 
| wxICON_STOP 
) ) { 
1029           s_bNoAsserts 
= TRUE
; 
1032         //case wxNO: nothing to do 
1037   s_bInAssert 
= FALSE
;