don't use static buffer needing a critical section to protect it for logging; this...
[wxWidgets.git] / src / common / log.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/log.cpp
3 // Purpose: Assorted wxLogXXX functions, and wxLog (sink for logs)
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 29/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_LOG
28
29 // wxWidgets
30 #ifndef WX_PRECOMP
31 #include "wx/log.h"
32 #include "wx/app.h"
33 #include "wx/arrstr.h"
34 #include "wx/intl.h"
35 #include "wx/string.h"
36 #include "wx/utils.h"
37 #endif //WX_PRECOMP
38
39 #include "wx/apptrait.h"
40 #include "wx/file.h"
41 #include "wx/msgout.h"
42 #include "wx/textfile.h"
43 #include "wx/thread.h"
44 #include "wx/wxchar.h"
45
46 // other standard headers
47 #ifndef __WXWINCE__
48 #include <errno.h>
49 #endif
50
51 #include <stdlib.h>
52
53 #ifndef __WXWINCE__
54 #include <time.h>
55 #else
56 #include "wx/msw/wince/time.h"
57 #endif
58
59 #if defined(__WINDOWS__)
60 #include "wx/msw/private.h" // includes windows.h
61 #endif
62
63 // ----------------------------------------------------------------------------
64 // non member functions
65 // ----------------------------------------------------------------------------
66
67 // define this to enable wrapping of log messages
68 //#define LOG_PRETTY_WRAP
69
70 #ifdef LOG_PRETTY_WRAP
71 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
72 #endif
73
74 // ============================================================================
75 // implementation
76 // ============================================================================
77
78 // ----------------------------------------------------------------------------
79 // implementation of Log functions
80 //
81 // NB: unfortunately we need all these distinct functions, we can't make them
82 // macros and not all compilers inline vararg functions.
83 // ----------------------------------------------------------------------------
84
85 // generic log function
86 void wxVLogGeneric(wxLogLevel level, const wxChar *szFormat, va_list argptr)
87 {
88 if ( wxLog::IsEnabled() ) {
89 wxLog::OnLog(level, wxString::FormatV(szFormat, argptr), time(NULL));
90 }
91 }
92
93 void wxLogGeneric(wxLogLevel level, const wxChar *szFormat, ...)
94 {
95 va_list argptr;
96 va_start(argptr, szFormat);
97 wxVLogGeneric(level, szFormat, argptr);
98 va_end(argptr);
99 }
100
101 #define IMPLEMENT_LOG_FUNCTION(level) \
102 void wxVLog##level(const wxChar *szFormat, va_list argptr) \
103 { \
104 if ( wxLog::IsEnabled() ) { \
105 wxLog::OnLog(wxLOG_##level, \
106 wxString::FormatV(szFormat, argptr), time(NULL));\
107 } \
108 } \
109 \
110 void wxLog##level(const wxChar *szFormat, ...) \
111 { \
112 va_list argptr; \
113 va_start(argptr, szFormat); \
114 wxVLog##level(szFormat, argptr); \
115 va_end(argptr); \
116 }
117
118 IMPLEMENT_LOG_FUNCTION(Error)
119 IMPLEMENT_LOG_FUNCTION(Warning)
120 IMPLEMENT_LOG_FUNCTION(Message)
121 IMPLEMENT_LOG_FUNCTION(Info)
122 IMPLEMENT_LOG_FUNCTION(Status)
123
124 void wxSafeShowMessage(const wxString& title, const wxString& text)
125 {
126 #ifdef __WINDOWS__
127 ::MessageBox(NULL, text, title, MB_OK | MB_ICONSTOP);
128 #else
129 wxFprintf(stderr, _T("%s: %s\n"), title.c_str(), text.c_str());
130 fflush(stderr);
131 #endif
132 }
133
134 // fatal errors can't be suppressed nor handled by the custom log target and
135 // always terminate the program
136 void wxVLogFatalError(const wxChar *szFormat, va_list argptr)
137 {
138 wxSafeShowMessage(_T("Fatal Error"), wxString::FormatV(szFormat, argptr));
139
140 #ifdef __WXWINCE__
141 ExitThread(3);
142 #else
143 abort();
144 #endif
145 }
146
147 void wxLogFatalError(const wxChar *szFormat, ...)
148 {
149 va_list argptr;
150 va_start(argptr, szFormat);
151 wxVLogFatalError(szFormat, argptr);
152
153 // some compilers warn about unreachable code and it shouldn't matter
154 // for the others anyhow...
155 //va_end(argptr);
156 }
157
158 // same as info, but only if 'verbose' mode is on
159 void wxVLogVerbose(const wxChar *szFormat, va_list argptr)
160 {
161 if ( wxLog::IsEnabled() ) {
162 if ( wxLog::GetActiveTarget() != NULL && wxLog::GetVerbose() ) {
163 wxLog::OnLog(wxLOG_Info,
164 wxString::FormatV(szFormat, argptr), time(NULL));
165 }
166 }
167 }
168
169 void wxLogVerbose(const wxChar *szFormat, ...)
170 {
171 va_list argptr;
172 va_start(argptr, szFormat);
173 wxVLogVerbose(szFormat, argptr);
174 va_end(argptr);
175 }
176
177 // debug functions
178 #ifdef __WXDEBUG__
179 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level) \
180 void wxVLog##level(const wxChar *szFormat, va_list argptr) \
181 { \
182 if ( wxLog::IsEnabled() ) { \
183 wxLog::OnLog(wxLOG_##level, \
184 wxString::FormatV(szFormat, argptr), time(NULL));\
185 } \
186 } \
187 \
188 void wxLog##level(const wxChar *szFormat, ...) \
189 { \
190 va_list argptr; \
191 va_start(argptr, szFormat); \
192 wxVLog##level(szFormat, argptr); \
193 va_end(argptr); \
194 }
195
196 void wxVLogTrace(const wxChar *mask, const wxChar *szFormat, va_list argptr)
197 {
198 if ( wxLog::IsEnabled() && wxLog::IsAllowedTraceMask(mask) ) {
199 wxString msg;
200 msg << _T("(") << mask << _T(") ") << wxString::FormatV(szFormat, argptr);
201
202 wxLog::OnLog(wxLOG_Trace, msg, time(NULL));
203 }
204 }
205
206 void wxLogTrace(const wxChar *mask, const wxChar *szFormat, ...)
207 {
208 va_list argptr;
209 va_start(argptr, szFormat);
210 wxVLogTrace(mask, szFormat, argptr);
211 va_end(argptr);
212 }
213
214 void wxVLogTrace(wxTraceMask mask, const wxChar *szFormat, va_list argptr)
215 {
216 // we check that all of mask bits are set in the current mask, so
217 // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something
218 // if both bits are set.
219 if ( wxLog::IsEnabled() && ((wxLog::GetTraceMask() & mask) == mask) ) {
220 wxLog::OnLog(wxLOG_Trace, wxString::FormatV(szFormat, argptr), time(NULL));
221 }
222 }
223
224 void wxLogTrace(wxTraceMask mask, const wxChar *szFormat, ...)
225 {
226 va_list argptr;
227 va_start(argptr, szFormat);
228 wxVLogTrace(mask, szFormat, argptr);
229 va_end(argptr);
230 }
231
232 #else // release
233 #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)
234 #endif
235
236 IMPLEMENT_LOG_DEBUG_FUNCTION(Debug)
237 IMPLEMENT_LOG_DEBUG_FUNCTION(Trace)
238
239 // wxLogSysError: one uses the last error code, for other you must give it
240 // explicitly
241
242 // return the system error message description
243 static inline wxString wxLogSysErrorHelper(long err)
244 {
245 return wxString::Format(_(" (error %ld: %s)"), err, wxSysErrorMsg(err));
246 }
247
248 void WXDLLEXPORT wxVLogSysError(const wxChar *szFormat, va_list argptr)
249 {
250 wxVLogSysError(wxSysErrorCode(), szFormat, argptr);
251 }
252
253 void WXDLLEXPORT wxLogSysError(const wxChar *szFormat, ...)
254 {
255 va_list argptr;
256 va_start(argptr, szFormat);
257 wxVLogSysError(szFormat, argptr);
258 va_end(argptr);
259 }
260
261 void WXDLLEXPORT wxVLogSysError(long err, const wxChar *fmt, va_list argptr)
262 {
263 if ( wxLog::IsEnabled() ) {
264 wxLog::OnLog(wxLOG_Error,
265 wxString::FormatV(fmt, argptr) + wxLogSysErrorHelper(err),
266 time(NULL));
267 }
268 }
269
270 void WXDLLEXPORT wxLogSysError(long lErrCode, const wxChar *szFormat, ...)
271 {
272 va_list argptr;
273 va_start(argptr, szFormat);
274 wxVLogSysError(lErrCode, szFormat, argptr);
275 va_end(argptr);
276 }
277
278 // ----------------------------------------------------------------------------
279 // wxLog class implementation
280 // ----------------------------------------------------------------------------
281
282 /* static */
283 unsigned wxLog::DoLogNumberOfRepeats()
284 {
285 long retval = ms_prevCounter;
286 wxLog *pLogger = GetActiveTarget();
287 if ( pLogger && ms_prevCounter > 0 )
288 {
289 wxString msg;
290 #if wxUSE_INTL
291 msg.Printf(wxPLURAL("The previous message repeated once.",
292 "The previous message repeated %lu times.",
293 ms_prevCounter),
294 ms_prevCounter);
295 #else
296 msg.Printf(wxT("The previous message was repeated."));
297 #endif
298 ms_prevCounter = 0;
299 ms_prevString.clear();
300 pLogger->DoLog(ms_prevLevel, msg.c_str(), ms_prevTimeStamp);
301 }
302 return retval;
303 }
304
305 wxLog::~wxLog()
306 {
307 if ( ms_prevCounter > 0 )
308 {
309 // looks like the repeat count has not been logged yet,
310 // so let's do it now
311 wxLog::DoLogNumberOfRepeats();
312 }
313 }
314
315 /* static */
316 void wxLog::OnLog(wxLogLevel level, const wxChar *szString, time_t t)
317 {
318 if ( IsEnabled() && ms_logLevel >= level )
319 {
320 wxLog *pLogger = GetActiveTarget();
321 if ( pLogger )
322 {
323 if ( GetRepetitionCounting() && ms_prevString == szString )
324 {
325 ms_prevCounter++;
326 }
327 else
328 {
329 if ( GetRepetitionCounting() )
330 {
331 pLogger->DoLogNumberOfRepeats();
332 }
333 ms_prevString = szString;
334 ms_prevLevel = level;
335 ms_prevTimeStamp = t;
336 pLogger->DoLog(level, szString, t);
337 }
338 }
339 }
340 }
341
342 // deprecated function
343 wxChar *wxLog::SetLogBuffer(wxChar * WXUNUSED(buf), size_t WXUNUSED(size))
344 {
345 return NULL;
346 }
347
348 wxLog *wxLog::GetActiveTarget()
349 {
350 if ( ms_bAutoCreate && ms_pLogger == NULL ) {
351 // prevent infinite recursion if someone calls wxLogXXX() from
352 // wxApp::CreateLogTarget()
353 static bool s_bInGetActiveTarget = false;
354 if ( !s_bInGetActiveTarget ) {
355 s_bInGetActiveTarget = true;
356
357 // ask the application to create a log target for us
358 if ( wxTheApp != NULL )
359 ms_pLogger = wxTheApp->GetTraits()->CreateLogTarget();
360 else
361 ms_pLogger = new wxLogStderr;
362
363 s_bInGetActiveTarget = false;
364
365 // do nothing if it fails - what can we do?
366 }
367 }
368
369 return ms_pLogger;
370 }
371
372 wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
373 {
374 if ( ms_pLogger != NULL ) {
375 // flush the old messages before changing because otherwise they might
376 // get lost later if this target is not restored
377 ms_pLogger->Flush();
378 }
379
380 wxLog *pOldLogger = ms_pLogger;
381 ms_pLogger = pLogger;
382
383 return pOldLogger;
384 }
385
386 void wxLog::DontCreateOnDemand()
387 {
388 ms_bAutoCreate = false;
389
390 // this is usually called at the end of the program and we assume that it
391 // is *always* called at the end - so we free memory here to avoid false
392 // memory leak reports from wxWin memory tracking code
393 ClearTraceMasks();
394 }
395
396 void wxLog::RemoveTraceMask(const wxString& str)
397 {
398 int index = ms_aTraceMasks.Index(str);
399 if ( index != wxNOT_FOUND )
400 ms_aTraceMasks.RemoveAt((size_t)index);
401 }
402
403 void wxLog::ClearTraceMasks()
404 {
405 ms_aTraceMasks.Clear();
406 }
407
408 void wxLog::TimeStamp(wxString *str)
409 {
410 if ( ms_timestamp )
411 {
412 wxChar buf[256];
413 time_t timeNow;
414 (void)time(&timeNow);
415 wxStrftime(buf, WXSIZEOF(buf), ms_timestamp, localtime(&timeNow));
416
417 str->Empty();
418 *str << buf << wxT(": ");
419 }
420 }
421
422 void wxLog::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
423 {
424 switch ( level ) {
425 case wxLOG_FatalError:
426 DoLogString(wxString(_("Fatal error: ")) + szString, t);
427 DoLogString(_("Program aborted."), t);
428 Flush();
429 #ifdef __WXWINCE__
430 ExitThread(3);
431 #else
432 abort();
433 #endif
434 break;
435
436 case wxLOG_Error:
437 DoLogString(wxString(_("Error: ")) + szString, t);
438 break;
439
440 case wxLOG_Warning:
441 DoLogString(wxString(_("Warning: ")) + szString, t);
442 break;
443
444 case wxLOG_Info:
445 if ( GetVerbose() )
446 case wxLOG_Message:
447 case wxLOG_Status:
448 default: // log unknown log levels too
449 DoLogString(szString, t);
450 break;
451
452 case wxLOG_Trace:
453 case wxLOG_Debug:
454 #ifdef __WXDEBUG__
455 {
456 wxString msg = level == wxLOG_Trace ? wxT("Trace: ")
457 : wxT("Debug: ");
458 msg << szString;
459 DoLogString(msg, t);
460 }
461 #endif // Debug
462 break;
463 }
464 }
465
466 void wxLog::DoLogString(const wxChar *WXUNUSED(szString), time_t WXUNUSED(t))
467 {
468 wxFAIL_MSG(wxT("DoLogString must be overriden if it's called."));
469 }
470
471 void wxLog::Flush()
472 {
473 // nothing to do here
474 }
475
476 /*static*/ bool wxLog::IsAllowedTraceMask(const wxChar *mask)
477 {
478 for ( wxArrayString::iterator it = ms_aTraceMasks.begin(),
479 en = ms_aTraceMasks.end();
480 it != en; ++it )
481 if ( *it == mask)
482 return true;
483 return false;
484 }
485
486 // ----------------------------------------------------------------------------
487 // wxLogBuffer implementation
488 // ----------------------------------------------------------------------------
489
490 void wxLogBuffer::Flush()
491 {
492 if ( !m_str.empty() )
493 {
494 wxMessageOutputBest out;
495 out.Printf(_T("%s"), m_str.c_str());
496 m_str.clear();
497 }
498 }
499
500 void wxLogBuffer::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
501 {
502 switch ( level )
503 {
504 case wxLOG_Trace:
505 case wxLOG_Debug:
506 #ifdef __WXDEBUG__
507 // don't put debug messages in the buffer, we don't want to show
508 // them to the user in a msg box, log them immediately
509 {
510 wxString str;
511 TimeStamp(&str);
512 str += szString;
513
514 wxMessageOutputDebug dbgout;
515 dbgout.Printf(_T("%s\n"), str.c_str());
516 }
517 #endif // __WXDEBUG__
518 break;
519
520 default:
521 wxLog::DoLog(level, szString, t);
522 }
523 }
524
525 void wxLogBuffer::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
526 {
527 m_str << szString << _T("\n");
528 }
529
530 // ----------------------------------------------------------------------------
531 // wxLogStderr class implementation
532 // ----------------------------------------------------------------------------
533
534 wxLogStderr::wxLogStderr(FILE *fp)
535 {
536 if ( fp == NULL )
537 m_fp = stderr;
538 else
539 m_fp = fp;
540 }
541
542 void wxLogStderr::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
543 {
544 wxString str;
545 TimeStamp(&str);
546 str << szString;
547
548 fputs(str.mb_str(), m_fp);
549 fputc(_T('\n'), m_fp);
550 fflush(m_fp);
551
552 // under GUI systems such as Windows or Mac, programs usually don't have
553 // stderr at all, so show the messages also somewhere else, typically in
554 // the debugger window so that they go at least somewhere instead of being
555 // simply lost
556 if ( m_fp == stderr )
557 {
558 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
559 if ( traits && !traits->HasStderr() )
560 {
561 wxMessageOutputDebug dbgout;
562 dbgout.Printf(_T("%s\n"), str.c_str());
563 }
564 }
565 }
566
567 // ----------------------------------------------------------------------------
568 // wxLogStream implementation
569 // ----------------------------------------------------------------------------
570
571 #if wxUSE_STD_IOSTREAM
572 #include "wx/ioswrap.h"
573 wxLogStream::wxLogStream(wxSTD ostream *ostr)
574 {
575 if ( ostr == NULL )
576 m_ostr = &wxSTD cerr;
577 else
578 m_ostr = ostr;
579 }
580
581 void wxLogStream::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
582 {
583 wxString str;
584 TimeStamp(&str);
585 (*m_ostr) << wxConvertWX2MB(str) << wxConvertWX2MB(szString) << wxSTD endl;
586 }
587 #endif // wxUSE_STD_IOSTREAM
588
589 // ----------------------------------------------------------------------------
590 // wxLogChain
591 // ----------------------------------------------------------------------------
592
593 wxLogChain::wxLogChain(wxLog *logger)
594 {
595 m_bPassMessages = true;
596
597 m_logNew = logger;
598 m_logOld = wxLog::SetActiveTarget(this);
599 }
600
601 wxLogChain::~wxLogChain()
602 {
603 delete m_logOld;
604
605 if ( m_logNew != this )
606 delete m_logNew;
607 }
608
609 void wxLogChain::SetLog(wxLog *logger)
610 {
611 if ( m_logNew != this )
612 delete m_logNew;
613
614 m_logNew = logger;
615 }
616
617 void wxLogChain::Flush()
618 {
619 if ( m_logOld )
620 m_logOld->Flush();
621
622 // be careful to avoid infinite recursion
623 if ( m_logNew && m_logNew != this )
624 m_logNew->Flush();
625 }
626
627 void wxLogChain::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
628 {
629 // let the previous logger show it
630 if ( m_logOld && IsPassingMessages() )
631 {
632 // bogus cast just to access protected DoLog
633 ((wxLogChain *)m_logOld)->DoLog(level, szString, t);
634 }
635
636 if ( m_logNew && m_logNew != this )
637 {
638 // as above...
639 ((wxLogChain *)m_logNew)->DoLog(level, szString, t);
640 }
641 }
642
643 // ----------------------------------------------------------------------------
644 // wxLogPassThrough
645 // ----------------------------------------------------------------------------
646
647 #ifdef __VISUALC__
648 // "'this' : used in base member initializer list" - so what?
649 #pragma warning(disable:4355)
650 #endif // VC++
651
652 wxLogPassThrough::wxLogPassThrough()
653 : wxLogChain(this)
654 {
655 }
656
657 #ifdef __VISUALC__
658 #pragma warning(default:4355)
659 #endif // VC++
660
661 // ============================================================================
662 // Global functions/variables
663 // ============================================================================
664
665 // ----------------------------------------------------------------------------
666 // static variables
667 // ----------------------------------------------------------------------------
668
669 bool wxLog::ms_bRepetCounting = false;
670 wxString wxLog::ms_prevString;
671 unsigned int wxLog::ms_prevCounter = 0;
672 time_t wxLog::ms_prevTimeStamp= 0;
673 wxLogLevel wxLog::ms_prevLevel;
674
675 wxLog *wxLog::ms_pLogger = (wxLog *)NULL;
676 bool wxLog::ms_doLog = true;
677 bool wxLog::ms_bAutoCreate = true;
678 bool wxLog::ms_bVerbose = false;
679
680 wxLogLevel wxLog::ms_logLevel = wxLOG_Max; // log everything by default
681
682 size_t wxLog::ms_suspendCount = 0;
683
684 const wxChar *wxLog::ms_timestamp = wxT("%X"); // time only, no date
685
686 wxTraceMask wxLog::ms_ulTraceMask = (wxTraceMask)0;
687 wxArrayString wxLog::ms_aTraceMasks;
688
689 // ----------------------------------------------------------------------------
690 // stdout error logging helper
691 // ----------------------------------------------------------------------------
692
693 // helper function: wraps the message and justifies it under given position
694 // (looks more pretty on the terminal). Also adds newline at the end.
695 //
696 // TODO this is now disabled until I find a portable way of determining the
697 // terminal window size (ok, I found it but does anybody really cares?)
698 #ifdef LOG_PRETTY_WRAP
699 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
700 {
701 size_t nMax = 80; // FIXME
702 size_t nStart = strlen(pszPrefix);
703 fputs(pszPrefix, f);
704
705 size_t n;
706 while ( *psz != '\0' ) {
707 for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
708 putc(*psz++, f);
709
710 // wrapped?
711 if ( *psz != '\0' ) {
712 /*putc('\n', f);*/
713 for ( n = 0; n < nStart; n++ )
714 putc(' ', f);
715
716 // as we wrapped, squeeze all white space
717 while ( isspace(*psz) )
718 psz++;
719 }
720 }
721
722 putc('\n', f);
723 }
724 #endif //LOG_PRETTY_WRAP
725
726 // ----------------------------------------------------------------------------
727 // error code/error message retrieval functions
728 // ----------------------------------------------------------------------------
729
730 // get error code from syste
731 unsigned long wxSysErrorCode()
732 {
733 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
734 return ::GetLastError();
735 #else //Unix
736 return errno;
737 #endif //Win/Unix
738 }
739
740 // get error message from system
741 const wxChar *wxSysErrorMsg(unsigned long nErrCode)
742 {
743 if ( nErrCode == 0 )
744 nErrCode = wxSysErrorCode();
745
746 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
747 static wxChar s_szBuf[1024];
748
749 // get error message from system
750 LPVOID lpMsgBuf;
751 if ( ::FormatMessage
752 (
753 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
754 NULL,
755 nErrCode,
756 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
757 (LPTSTR)&lpMsgBuf,
758 0,
759 NULL
760 ) == 0 )
761 {
762 // if this happens, something is seriously wrong, so don't use _() here
763 // for safety
764 wxSprintf(s_szBuf, _T("unknown error %lx"), nErrCode);
765 return s_szBuf;
766 }
767
768
769 // copy it to our buffer and free memory
770 // Crashes on SmartPhone (FIXME)
771 #if !defined(__SMARTPHONE__) /* of WinCE */
772 if( lpMsgBuf != 0 )
773 {
774 wxStrncpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf) - 1);
775 s_szBuf[WXSIZEOF(s_szBuf) - 1] = wxT('\0');
776
777 LocalFree(lpMsgBuf);
778
779 // returned string is capitalized and ended with '\r\n' - bad
780 s_szBuf[0] = (wxChar)wxTolower(s_szBuf[0]);
781 size_t len = wxStrlen(s_szBuf);
782 if ( len > 0 ) {
783 // truncate string
784 if ( s_szBuf[len - 2] == wxT('\r') )
785 s_szBuf[len - 2] = wxT('\0');
786 }
787 }
788 else
789 #endif // !__SMARTPHONE__
790 {
791 s_szBuf[0] = wxT('\0');
792 }
793
794 return s_szBuf;
795 #else // !__WXMSW__
796 #if wxUSE_UNICODE
797 static wchar_t s_wzBuf[1024];
798 wxConvCurrent->MB2WC(s_wzBuf, strerror((int)nErrCode),
799 WXSIZEOF(s_wzBuf) - 1);
800 return s_wzBuf;
801 #else
802 return strerror((int)nErrCode);
803 #endif
804 #endif // __WXMSW__/!__WXMSW__
805 }
806
807 #endif // wxUSE_LOG