]> git.saurik.com Git - wxWidgets.git/blob - src/common/log.cpp
wx{V,H}SCROLL code is needed on all platforms
[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 #if WXWIN_COMPATIBILITY_2_6
344
345 wxChar *wxLog::SetLogBuffer(wxChar * WXUNUSED(buf), size_t WXUNUSED(size))
346 {
347 return NULL;
348 }
349
350 #endif // WXWIN_COMPATIBILITY_2_6
351
352 wxLog *wxLog::GetActiveTarget()
353 {
354 if ( ms_bAutoCreate && ms_pLogger == NULL ) {
355 // prevent infinite recursion if someone calls wxLogXXX() from
356 // wxApp::CreateLogTarget()
357 static bool s_bInGetActiveTarget = false;
358 if ( !s_bInGetActiveTarget ) {
359 s_bInGetActiveTarget = true;
360
361 // ask the application to create a log target for us
362 if ( wxTheApp != NULL )
363 ms_pLogger = wxTheApp->GetTraits()->CreateLogTarget();
364 else
365 ms_pLogger = new wxLogStderr;
366
367 s_bInGetActiveTarget = false;
368
369 // do nothing if it fails - what can we do?
370 }
371 }
372
373 return ms_pLogger;
374 }
375
376 wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
377 {
378 if ( ms_pLogger != NULL ) {
379 // flush the old messages before changing because otherwise they might
380 // get lost later if this target is not restored
381 ms_pLogger->Flush();
382 }
383
384 wxLog *pOldLogger = ms_pLogger;
385 ms_pLogger = pLogger;
386
387 return pOldLogger;
388 }
389
390 void wxLog::DontCreateOnDemand()
391 {
392 ms_bAutoCreate = false;
393
394 // this is usually called at the end of the program and we assume that it
395 // is *always* called at the end - so we free memory here to avoid false
396 // memory leak reports from wxWin memory tracking code
397 ClearTraceMasks();
398 }
399
400 void wxLog::RemoveTraceMask(const wxString& str)
401 {
402 int index = ms_aTraceMasks.Index(str);
403 if ( index != wxNOT_FOUND )
404 ms_aTraceMasks.RemoveAt((size_t)index);
405 }
406
407 void wxLog::ClearTraceMasks()
408 {
409 ms_aTraceMasks.Clear();
410 }
411
412 void wxLog::TimeStamp(wxString *str)
413 {
414 if ( ms_timestamp )
415 {
416 wxChar buf[256];
417 time_t timeNow;
418 (void)time(&timeNow);
419 wxStrftime(buf, WXSIZEOF(buf), ms_timestamp, localtime(&timeNow));
420
421 str->Empty();
422 *str << buf << wxT(": ");
423 }
424 }
425
426 void wxLog::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
427 {
428 switch ( level ) {
429 case wxLOG_FatalError:
430 DoLogString(wxString(_("Fatal error: ")) + szString, t);
431 DoLogString(_("Program aborted."), t);
432 Flush();
433 #ifdef __WXWINCE__
434 ExitThread(3);
435 #else
436 abort();
437 #endif
438 break;
439
440 case wxLOG_Error:
441 DoLogString(wxString(_("Error: ")) + szString, t);
442 break;
443
444 case wxLOG_Warning:
445 DoLogString(wxString(_("Warning: ")) + szString, t);
446 break;
447
448 case wxLOG_Info:
449 if ( GetVerbose() )
450 case wxLOG_Message:
451 case wxLOG_Status:
452 default: // log unknown log levels too
453 DoLogString(szString, t);
454 break;
455
456 case wxLOG_Trace:
457 case wxLOG_Debug:
458 #ifdef __WXDEBUG__
459 {
460 wxString msg = level == wxLOG_Trace ? wxT("Trace: ")
461 : wxT("Debug: ");
462 msg << szString;
463 DoLogString(msg, t);
464 }
465 #endif // Debug
466 break;
467 }
468 }
469
470 void wxLog::DoLogString(const wxChar *WXUNUSED(szString), time_t WXUNUSED(t))
471 {
472 wxFAIL_MSG(wxT("DoLogString must be overriden if it's called."));
473 }
474
475 void wxLog::Flush()
476 {
477 // nothing to do here
478 }
479
480 /*static*/ bool wxLog::IsAllowedTraceMask(const wxChar *mask)
481 {
482 for ( wxArrayString::iterator it = ms_aTraceMasks.begin(),
483 en = ms_aTraceMasks.end();
484 it != en; ++it )
485 if ( *it == mask)
486 return true;
487 return false;
488 }
489
490 // ----------------------------------------------------------------------------
491 // wxLogBuffer implementation
492 // ----------------------------------------------------------------------------
493
494 void wxLogBuffer::Flush()
495 {
496 if ( !m_str.empty() )
497 {
498 wxMessageOutputBest out;
499 out.Printf(_T("%s"), m_str.c_str());
500 m_str.clear();
501 }
502 }
503
504 void wxLogBuffer::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
505 {
506 switch ( level )
507 {
508 case wxLOG_Trace:
509 case wxLOG_Debug:
510 #ifdef __WXDEBUG__
511 // don't put debug messages in the buffer, we don't want to show
512 // them to the user in a msg box, log them immediately
513 {
514 wxString str;
515 TimeStamp(&str);
516 str += szString;
517
518 wxMessageOutputDebug dbgout;
519 dbgout.Printf(_T("%s\n"), str.c_str());
520 }
521 #endif // __WXDEBUG__
522 break;
523
524 default:
525 wxLog::DoLog(level, szString, t);
526 }
527 }
528
529 void wxLogBuffer::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
530 {
531 m_str << szString << _T("\n");
532 }
533
534 // ----------------------------------------------------------------------------
535 // wxLogStderr class implementation
536 // ----------------------------------------------------------------------------
537
538 wxLogStderr::wxLogStderr(FILE *fp)
539 {
540 if ( fp == NULL )
541 m_fp = stderr;
542 else
543 m_fp = fp;
544 }
545
546 void wxLogStderr::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
547 {
548 wxString str;
549 TimeStamp(&str);
550 str << szString;
551
552 fputs(str.mb_str(), m_fp);
553 fputc(_T('\n'), m_fp);
554 fflush(m_fp);
555
556 // under GUI systems such as Windows or Mac, programs usually don't have
557 // stderr at all, so show the messages also somewhere else, typically in
558 // the debugger window so that they go at least somewhere instead of being
559 // simply lost
560 if ( m_fp == stderr )
561 {
562 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
563 if ( traits && !traits->HasStderr() )
564 {
565 wxMessageOutputDebug dbgout;
566 dbgout.Printf(_T("%s\n"), str.c_str());
567 }
568 }
569 }
570
571 // ----------------------------------------------------------------------------
572 // wxLogStream implementation
573 // ----------------------------------------------------------------------------
574
575 #if wxUSE_STD_IOSTREAM
576 #include "wx/ioswrap.h"
577 wxLogStream::wxLogStream(wxSTD ostream *ostr)
578 {
579 if ( ostr == NULL )
580 m_ostr = &wxSTD cerr;
581 else
582 m_ostr = ostr;
583 }
584
585 void wxLogStream::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
586 {
587 wxString str;
588 TimeStamp(&str);
589 (*m_ostr) << wxConvertWX2MB(str) << wxConvertWX2MB(szString) << wxSTD endl;
590 }
591 #endif // wxUSE_STD_IOSTREAM
592
593 // ----------------------------------------------------------------------------
594 // wxLogChain
595 // ----------------------------------------------------------------------------
596
597 wxLogChain::wxLogChain(wxLog *logger)
598 {
599 m_bPassMessages = true;
600
601 m_logNew = logger;
602 m_logOld = wxLog::SetActiveTarget(this);
603 }
604
605 wxLogChain::~wxLogChain()
606 {
607 delete m_logOld;
608
609 if ( m_logNew != this )
610 delete m_logNew;
611 }
612
613 void wxLogChain::SetLog(wxLog *logger)
614 {
615 if ( m_logNew != this )
616 delete m_logNew;
617
618 m_logNew = logger;
619 }
620
621 void wxLogChain::Flush()
622 {
623 if ( m_logOld )
624 m_logOld->Flush();
625
626 // be careful to avoid infinite recursion
627 if ( m_logNew && m_logNew != this )
628 m_logNew->Flush();
629 }
630
631 void wxLogChain::DoLog(wxLogLevel level, const wxChar *szString, time_t t)
632 {
633 // let the previous logger show it
634 if ( m_logOld && IsPassingMessages() )
635 {
636 // bogus cast just to access protected DoLog
637 ((wxLogChain *)m_logOld)->DoLog(level, szString, t);
638 }
639
640 if ( m_logNew && m_logNew != this )
641 {
642 // as above...
643 ((wxLogChain *)m_logNew)->DoLog(level, szString, t);
644 }
645 }
646
647 // ----------------------------------------------------------------------------
648 // wxLogPassThrough
649 // ----------------------------------------------------------------------------
650
651 #ifdef __VISUALC__
652 // "'this' : used in base member initializer list" - so what?
653 #pragma warning(disable:4355)
654 #endif // VC++
655
656 wxLogPassThrough::wxLogPassThrough()
657 : wxLogChain(this)
658 {
659 }
660
661 #ifdef __VISUALC__
662 #pragma warning(default:4355)
663 #endif // VC++
664
665 // ============================================================================
666 // Global functions/variables
667 // ============================================================================
668
669 // ----------------------------------------------------------------------------
670 // static variables
671 // ----------------------------------------------------------------------------
672
673 bool wxLog::ms_bRepetCounting = false;
674 wxString wxLog::ms_prevString;
675 unsigned int wxLog::ms_prevCounter = 0;
676 time_t wxLog::ms_prevTimeStamp= 0;
677 wxLogLevel wxLog::ms_prevLevel;
678
679 wxLog *wxLog::ms_pLogger = (wxLog *)NULL;
680 bool wxLog::ms_doLog = true;
681 bool wxLog::ms_bAutoCreate = true;
682 bool wxLog::ms_bVerbose = false;
683
684 wxLogLevel wxLog::ms_logLevel = wxLOG_Max; // log everything by default
685
686 size_t wxLog::ms_suspendCount = 0;
687
688 const wxChar *wxLog::ms_timestamp = wxT("%X"); // time only, no date
689
690 wxTraceMask wxLog::ms_ulTraceMask = (wxTraceMask)0;
691 wxArrayString wxLog::ms_aTraceMasks;
692
693 // ----------------------------------------------------------------------------
694 // stdout error logging helper
695 // ----------------------------------------------------------------------------
696
697 // helper function: wraps the message and justifies it under given position
698 // (looks more pretty on the terminal). Also adds newline at the end.
699 //
700 // TODO this is now disabled until I find a portable way of determining the
701 // terminal window size (ok, I found it but does anybody really cares?)
702 #ifdef LOG_PRETTY_WRAP
703 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
704 {
705 size_t nMax = 80; // FIXME
706 size_t nStart = strlen(pszPrefix);
707 fputs(pszPrefix, f);
708
709 size_t n;
710 while ( *psz != '\0' ) {
711 for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
712 putc(*psz++, f);
713
714 // wrapped?
715 if ( *psz != '\0' ) {
716 /*putc('\n', f);*/
717 for ( n = 0; n < nStart; n++ )
718 putc(' ', f);
719
720 // as we wrapped, squeeze all white space
721 while ( isspace(*psz) )
722 psz++;
723 }
724 }
725
726 putc('\n', f);
727 }
728 #endif //LOG_PRETTY_WRAP
729
730 // ----------------------------------------------------------------------------
731 // error code/error message retrieval functions
732 // ----------------------------------------------------------------------------
733
734 // get error code from syste
735 unsigned long wxSysErrorCode()
736 {
737 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
738 return ::GetLastError();
739 #else //Unix
740 return errno;
741 #endif //Win/Unix
742 }
743
744 // get error message from system
745 const wxChar *wxSysErrorMsg(unsigned long nErrCode)
746 {
747 if ( nErrCode == 0 )
748 nErrCode = wxSysErrorCode();
749
750 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
751 static wxChar s_szBuf[1024];
752
753 // get error message from system
754 LPVOID lpMsgBuf;
755 if ( ::FormatMessage
756 (
757 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
758 NULL,
759 nErrCode,
760 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
761 (LPTSTR)&lpMsgBuf,
762 0,
763 NULL
764 ) == 0 )
765 {
766 // if this happens, something is seriously wrong, so don't use _() here
767 // for safety
768 wxSprintf(s_szBuf, _T("unknown error %lx"), nErrCode);
769 return s_szBuf;
770 }
771
772
773 // copy it to our buffer and free memory
774 // Crashes on SmartPhone (FIXME)
775 #if !defined(__SMARTPHONE__) /* of WinCE */
776 if( lpMsgBuf != 0 )
777 {
778 wxStrncpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf) - 1);
779 s_szBuf[WXSIZEOF(s_szBuf) - 1] = wxT('\0');
780
781 LocalFree(lpMsgBuf);
782
783 // returned string is capitalized and ended with '\r\n' - bad
784 s_szBuf[0] = (wxChar)wxTolower(s_szBuf[0]);
785 size_t len = wxStrlen(s_szBuf);
786 if ( len > 0 ) {
787 // truncate string
788 if ( s_szBuf[len - 2] == wxT('\r') )
789 s_szBuf[len - 2] = wxT('\0');
790 }
791 }
792 else
793 #endif // !__SMARTPHONE__
794 {
795 s_szBuf[0] = wxT('\0');
796 }
797
798 return s_szBuf;
799 #else // !__WXMSW__
800 #if wxUSE_UNICODE
801 static wchar_t s_wzBuf[1024];
802 wxConvCurrent->MB2WC(s_wzBuf, strerror((int)nErrCode),
803 WXSIZEOF(s_wzBuf) - 1);
804 return s_wzBuf;
805 #else
806 return strerror((int)nErrCode);
807 #endif
808 #endif // __WXMSW__/!__WXMSW__
809 }
810
811 #endif // wxUSE_LOG