]> git.saurik.com Git - wxWidgets.git/blob - src/common/log.cpp
Made wxLogXXX() functions thread-safe.
[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/datetime.h"
41 #include "wx/file.h"
42 #include "wx/msgout.h"
43 #include "wx/textfile.h"
44 #include "wx/thread.h"
45 #include "wx/crt.h"
46 #include "wx/vector.h"
47
48 // other standard headers
49 #ifndef __WXWINCE__
50 #include <errno.h>
51 #endif
52
53 #include <stdlib.h>
54
55 #ifndef __WXPALMOS5__
56 #ifndef __WXWINCE__
57 #include <time.h>
58 #else
59 #include "wx/msw/wince/time.h"
60 #endif
61 #endif /* ! __WXPALMOS5__ */
62
63 #if defined(__WINDOWS__)
64 #include "wx/msw/private.h" // includes windows.h
65 #endif
66
67 #undef wxLOG_COMPONENT
68 const char *wxLOG_COMPONENT = "";
69
70 #if wxUSE_THREADS
71
72 namespace
73 {
74
75 // contains messages logged by the other threads and waiting to be shown until
76 // Flush() is called in the main one
77 typedef wxVector<wxLogRecord> wxLogRecords;
78 wxLogRecords gs_bufferedLogRecords;
79
80
81 // define static functions providing access to the critical sections we use
82 // instead of just using static critical section variables as log functions may
83 // be used during static initialization and while this is certainly not
84 // advisable it's still better to not crash (as we'd do if we used a yet
85 // uninitialized critical section) if it happens
86
87 // this critical section is used for buffering the messages from threads other
88 // than main, i.e. it protects all accesses to gs_bufferedLogRecords above
89 inline wxCriticalSection& GetBackgroundLogCS()
90 {
91 static wxCriticalSection s_csBackground;
92
93 return s_csBackground;
94 }
95
96 inline wxCriticalSection& GetTraceMaskCS()
97 {
98 static wxCriticalSection s_csTrace;
99
100 return s_csTrace;
101 }
102
103 inline wxCriticalSection& GetPreviousLogCS()
104 {
105 static wxCriticalSection s_csPrev;
106
107 return s_csPrev;
108 }
109
110 inline wxCriticalSection& GetLevelsCS()
111 {
112 static wxCriticalSection s_csLevels;
113
114 return s_csLevels;
115 }
116
117 } // anonymous namespace
118
119 #endif // wxUSE_THREADS
120
121 // ----------------------------------------------------------------------------
122 // non member functions
123 // ----------------------------------------------------------------------------
124
125 // define this to enable wrapping of log messages
126 //#define LOG_PRETTY_WRAP
127
128 #ifdef LOG_PRETTY_WRAP
129 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
130 #endif
131
132 // ----------------------------------------------------------------------------
133 // module globals
134 // ----------------------------------------------------------------------------
135
136 namespace
137 {
138
139 // this struct is used to store information about the previous log message used
140 // by OnLog() to (optionally) avoid logging multiple copies of the same message
141 struct PreviousLogInfo
142 {
143 PreviousLogInfo()
144 {
145 numRepeated = 0;
146 }
147
148
149 // previous message itself
150 wxString msg;
151
152 // its level
153 wxLogLevel level;
154
155 // other information about it
156 wxLogRecordInfo info;
157
158 // the number of times it was already repeated
159 unsigned numRepeated;
160 };
161
162 PreviousLogInfo gs_prevLog;
163
164
165 // map containing all components for which log level was explicitly set
166 //
167 // NB: all accesses to it must be protected by GetLevelsCS() critical section
168 wxStringToNumHashMap gs_componentLevels;
169
170 } // anonymous namespace
171
172 // ============================================================================
173 // implementation
174 // ============================================================================
175
176 // ----------------------------------------------------------------------------
177 // helper global functions
178 // ----------------------------------------------------------------------------
179
180 void wxSafeShowMessage(const wxString& title, const wxString& text)
181 {
182 #ifdef __WINDOWS__
183 ::MessageBox(NULL, text.t_str(), title.t_str(), MB_OK | MB_ICONSTOP);
184 #else
185 wxFprintf(stderr, wxS("%s: %s\n"), title.c_str(), text.c_str());
186 fflush(stderr);
187 #endif
188 }
189
190 // ----------------------------------------------------------------------------
191 // wxLog class implementation
192 // ----------------------------------------------------------------------------
193
194 unsigned wxLog::LogLastRepeatIfNeeded()
195 {
196 wxCRIT_SECT_LOCKER(lock, GetPreviousLogCS());
197
198 return LogLastRepeatIfNeededUnlocked();
199 }
200
201 unsigned wxLog::LogLastRepeatIfNeededUnlocked()
202 {
203 const unsigned count = gs_prevLog.numRepeated;
204
205 if ( gs_prevLog.numRepeated )
206 {
207 wxString msg;
208 #if wxUSE_INTL
209 msg.Printf(wxPLURAL("The previous message repeated once.",
210 "The previous message repeated %lu times.",
211 gs_prevLog.numRepeated),
212 gs_prevLog.numRepeated);
213 #else
214 msg.Printf(wxS("The previous message was repeated %lu times."),
215 gs_prevLog.numRepeated);
216 #endif
217 gs_prevLog.numRepeated = 0;
218 gs_prevLog.msg.clear();
219 DoLogRecord(gs_prevLog.level, msg, gs_prevLog.info);
220 }
221
222 return count;
223 }
224
225 wxLog::~wxLog()
226 {
227 // Flush() must be called before destroying the object as otherwise some
228 // messages could be lost
229 if ( gs_prevLog.numRepeated )
230 {
231 wxMessageOutputDebug().Printf
232 (
233 wxS("Last repeated message (\"%s\", %lu times) wasn't output"),
234 gs_prevLog.msg,
235 gs_prevLog.numRepeated
236 );
237 }
238 }
239
240 // ----------------------------------------------------------------------------
241 // wxLog logging functions
242 // ----------------------------------------------------------------------------
243
244 /* static */
245 void
246 wxLog::OnLog(wxLogLevel level, const wxString& msg, time_t t)
247 {
248 wxLogRecordInfo info;
249 info.timestamp = t;
250 #if wxUSE_THREADS
251 info.threadId = wxThread::GetCurrentId();
252 #endif // wxUSE_THREADS
253
254 OnLog(level, msg, info);
255 }
256
257 /* static */
258 void
259 wxLog::OnLog(wxLogLevel level,
260 const wxString& msg,
261 const wxLogRecordInfo& info)
262 {
263 // fatal errors can't be suppressed nor handled by the custom log target
264 // and always terminate the program
265 if ( level == wxLOG_FatalError )
266 {
267 wxSafeShowMessage(wxS("Fatal Error"), msg);
268
269 #ifdef __WXWINCE__
270 ExitThread(3);
271 #else
272 abort();
273 #endif
274 }
275
276 wxLog *pLogger = GetActiveTarget();
277 if ( !pLogger )
278 return;
279
280 #if wxUSE_THREADS
281 if ( !wxThread::IsMain() )
282 {
283 wxCriticalSectionLocker lock(GetBackgroundLogCS());
284
285 gs_bufferedLogRecords.push_back(wxLogRecord(level, msg, info));
286
287 // ensure that our Flush() will be called soon
288 wxWakeUpIdle();
289
290 return;
291 }
292 #endif // wxUSE_THREADS
293
294 pLogger->OnLogInMainThread(level, msg, info);
295 }
296
297 void
298 wxLog::OnLogInMainThread(wxLogLevel level,
299 const wxString& msg,
300 const wxLogRecordInfo& info)
301 {
302 if ( GetRepetitionCounting() )
303 {
304 wxCRIT_SECT_LOCKER(lock, GetPreviousLogCS());
305
306 if ( msg == gs_prevLog.msg )
307 {
308 gs_prevLog.numRepeated++;
309
310 // nothing else to do, in particular, don't log the
311 // repeated message
312 return;
313 }
314
315 LogLastRepeatIfNeededUnlocked();
316
317 // reset repetition counter for a new message
318 gs_prevLog.msg = msg;
319 gs_prevLog.level = level;
320 gs_prevLog.info = info;
321 }
322
323 // handle extra data which may be passed to us by wxLogXXX()
324 wxString prefix, suffix;
325 wxUIntPtr num = 0;
326 if ( info.GetNumValue(wxLOG_KEY_SYS_ERROR_CODE, &num) )
327 {
328 long err = static_cast<long>(num);
329 if ( !err )
330 err = wxSysErrorCode();
331
332 suffix.Printf(_(" (error %ld: %s)"), err, wxSysErrorMsg(err));
333 }
334
335 #if wxUSE_LOG_TRACE
336 wxString str;
337 if ( level == wxLOG_Trace && info.GetStrValue(wxLOG_KEY_TRACE_MASK, &str) )
338 {
339 prefix = "(" + str + ") ";
340 }
341 #endif // wxUSE_LOG_TRACE
342
343 DoLogRecord(level, prefix + msg + suffix, info);
344 }
345
346 void wxLog::DoLogRecord(wxLogLevel level,
347 const wxString& msg,
348 const wxLogRecordInfo& info)
349 {
350 #if WXWIN_COMPATIBILITY_2_8
351 // call the old DoLog() to ensure that existing custom log classes still
352 // work
353 //
354 // as the user code could have defined it as either taking "const char *"
355 // (in ANSI build) or "const wxChar *" (in ANSI/Unicode), we have no choice
356 // but to call both of them
357 DoLog(level, (const char*)msg.mb_str(), info.timestamp);
358 DoLog(level, (const wchar_t*)msg.wc_str(), info.timestamp);
359 #endif // WXWIN_COMPATIBILITY_2_8
360
361
362 // TODO: it would be better to extract message formatting in a separate
363 // wxLogFormatter class but for now we hard code formatting here
364
365 wxString prefix;
366
367 // don't time stamp debug messages under MSW as debug viewers usually
368 // already have an option to do it
369 #ifdef __WXMSW__
370 if ( level != wxLOG_Debug && level != wxLOG_Trace )
371 #endif // __WXMSW__
372 TimeStamp(&prefix);
373
374 // TODO: use the other wxLogRecordInfo fields
375
376 switch ( level )
377 {
378 case wxLOG_Error:
379 prefix += _("Error: ");
380 break;
381
382 case wxLOG_Warning:
383 prefix += _("Warning: ");
384 break;
385
386 // don't prepend "debug/trace" prefix under MSW as it goes to the debug
387 // window anyhow and so can't be confused with something else
388 #ifndef __WXMSW__
389 case wxLOG_Debug:
390 // this prefix (as well as the one below) is intentionally not
391 // translated as nobody translates debug messages anyhow
392 prefix += "Debug: ";
393 break;
394
395 case wxLOG_Trace:
396 prefix += "Trace: ";
397 break;
398 #endif // !__WXMSW__
399 }
400
401 DoLogTextAtLevel(level, prefix + msg);
402 }
403
404 void wxLog::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
405 {
406 // we know about debug messages (because using wxMessageOutputDebug is the
407 // right thing to do in 99% of all cases and also for compatibility) but
408 // anything else needs to be handled in the derived class
409 if ( level == wxLOG_Debug || level == wxLOG_Trace )
410 {
411 wxMessageOutputDebug().Output(msg + wxS('\n'));
412 }
413 else
414 {
415 DoLogText(msg);
416 }
417 }
418
419 void wxLog::DoLogText(const wxString& WXUNUSED(msg))
420 {
421 // in 2.8-compatible build the derived class might override DoLog() or
422 // DoLogString() instead so we can't have this assert there
423 #if !WXWIN_COMPATIBILITY_2_8
424 wxFAIL_MSG( "must be overridden if it is called" );
425 #endif // WXWIN_COMPATIBILITY_2_8
426 }
427
428 #if WXWIN_COMPATIBILITY_2_8
429
430 void wxLog::DoLog(wxLogLevel WXUNUSED(level), const char *szString, time_t t)
431 {
432 DoLogString(szString, t);
433 }
434
435 void wxLog::DoLog(wxLogLevel WXUNUSED(level), const wchar_t *wzString, time_t t)
436 {
437 DoLogString(wzString, t);
438 }
439
440 #endif // WXWIN_COMPATIBILITY_2_8
441
442 // ----------------------------------------------------------------------------
443 // wxLog active target management
444 // ----------------------------------------------------------------------------
445
446 wxLog *wxLog::GetActiveTarget()
447 {
448 if ( ms_bAutoCreate && ms_pLogger == NULL ) {
449 // prevent infinite recursion if someone calls wxLogXXX() from
450 // wxApp::CreateLogTarget()
451 static bool s_bInGetActiveTarget = false;
452 if ( !s_bInGetActiveTarget ) {
453 s_bInGetActiveTarget = true;
454
455 // ask the application to create a log target for us
456 if ( wxTheApp != NULL )
457 ms_pLogger = wxTheApp->GetTraits()->CreateLogTarget();
458 else
459 ms_pLogger = new wxLogStderr;
460
461 s_bInGetActiveTarget = false;
462
463 // do nothing if it fails - what can we do?
464 }
465 }
466
467 return ms_pLogger;
468 }
469
470 wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
471 {
472 if ( ms_pLogger != NULL ) {
473 // flush the old messages before changing because otherwise they might
474 // get lost later if this target is not restored
475 ms_pLogger->Flush();
476 }
477
478 wxLog *pOldLogger = ms_pLogger;
479 ms_pLogger = pLogger;
480
481 return pOldLogger;
482 }
483
484 void wxLog::DontCreateOnDemand()
485 {
486 ms_bAutoCreate = false;
487
488 // this is usually called at the end of the program and we assume that it
489 // is *always* called at the end - so we free memory here to avoid false
490 // memory leak reports from wxWin memory tracking code
491 ClearTraceMasks();
492 }
493
494 void wxLog::DoCreateOnDemand()
495 {
496 ms_bAutoCreate = true;
497 }
498
499 // ----------------------------------------------------------------------------
500 // wxLog components levels
501 // ----------------------------------------------------------------------------
502
503 /* static */
504 void wxLog::SetComponentLevel(const wxString& component, wxLogLevel level)
505 {
506 if ( component.empty() )
507 {
508 SetLogLevel(level);
509 }
510 else
511 {
512 wxCRIT_SECT_LOCKER(lock, GetLevelsCS());
513
514 gs_componentLevels[component] = level;
515 }
516 }
517
518 /* static */
519 wxLogLevel wxLog::GetComponentLevel(wxString component)
520 {
521 wxCRIT_SECT_LOCKER(lock, GetLevelsCS());
522
523 while ( !component.empty() )
524 {
525 wxStringToNumHashMap::const_iterator
526 it = gs_componentLevels.find(component);
527 if ( it != gs_componentLevels.end() )
528 return static_cast<wxLogLevel>(it->second);
529
530 component = component.BeforeLast('/');
531 }
532
533 return GetLogLevel();
534 }
535
536 // ----------------------------------------------------------------------------
537 // wxLog trace masks
538 // ----------------------------------------------------------------------------
539
540 void wxLog::AddTraceMask(const wxString& str)
541 {
542 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
543
544 ms_aTraceMasks.push_back(str);
545 }
546
547 void wxLog::RemoveTraceMask(const wxString& str)
548 {
549 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
550
551 int index = ms_aTraceMasks.Index(str);
552 if ( index != wxNOT_FOUND )
553 ms_aTraceMasks.RemoveAt((size_t)index);
554 }
555
556 void wxLog::ClearTraceMasks()
557 {
558 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
559
560 ms_aTraceMasks.Clear();
561 }
562
563 /*static*/ bool wxLog::IsAllowedTraceMask(const wxString& mask)
564 {
565 wxCRIT_SECT_LOCKER(lock, GetTraceMaskCS());
566
567 for ( wxArrayString::iterator it = ms_aTraceMasks.begin(),
568 en = ms_aTraceMasks.end();
569 it != en; ++it )
570 {
571 if ( *it == mask)
572 return true;
573 }
574
575 return false;
576 }
577
578 // ----------------------------------------------------------------------------
579 // wxLog miscellaneous other methods
580 // ----------------------------------------------------------------------------
581
582 void wxLog::TimeStamp(wxString *str)
583 {
584 #if wxUSE_DATETIME
585 if ( !ms_timestamp.empty() )
586 {
587 wxChar buf[256];
588 time_t timeNow;
589 (void)time(&timeNow);
590
591 struct tm tm;
592 wxStrftime(buf, WXSIZEOF(buf),
593 ms_timestamp, wxLocaltime_r(&timeNow, &tm));
594
595 str->Empty();
596 *str << buf << wxS(": ");
597 }
598 #endif // wxUSE_DATETIME
599 }
600
601 void wxLog::Flush()
602 {
603 #if wxUSE_THREADS
604 wxASSERT_MSG( wxThread::IsMain(),
605 "should be called from the main thread only" );
606
607 // check if we have queued messages from other threads
608 wxLogRecords bufferedLogRecords;
609
610 {
611 wxCriticalSectionLocker lock(GetBackgroundLogCS());
612 bufferedLogRecords.swap(gs_bufferedLogRecords);
613
614 // release the lock now to not keep it while we are logging the
615 // messages below, allowing background threads to run
616 }
617
618 if ( !bufferedLogRecords.empty() )
619 {
620 for ( wxLogRecords::const_iterator it = bufferedLogRecords.begin();
621 it != bufferedLogRecords.end();
622 ++it )
623 {
624 OnLogInMainThread(it->level, it->msg, it->info);
625 }
626 }
627 #endif // wxUSE_THREADS
628
629 LogLastRepeatIfNeeded();
630 }
631
632 // ----------------------------------------------------------------------------
633 // wxLogBuffer implementation
634 // ----------------------------------------------------------------------------
635
636 void wxLogBuffer::Flush()
637 {
638 wxLog::Flush();
639
640 if ( !m_str.empty() )
641 {
642 wxMessageOutputBest out;
643 out.Printf(wxS("%s"), m_str.c_str());
644 m_str.clear();
645 }
646 }
647
648 void wxLogBuffer::DoLogTextAtLevel(wxLogLevel level, const wxString& msg)
649 {
650 // don't put debug messages in the buffer, we don't want to show
651 // them to the user in a msg box, log them immediately
652 switch ( level )
653 {
654 case wxLOG_Debug:
655 case wxLOG_Trace:
656 wxLog::DoLogTextAtLevel(level, msg);
657 break;
658
659 default:
660 m_str << msg << wxS("\n");
661 }
662 }
663
664 // ----------------------------------------------------------------------------
665 // wxLogStderr class implementation
666 // ----------------------------------------------------------------------------
667
668 wxLogStderr::wxLogStderr(FILE *fp)
669 {
670 if ( fp == NULL )
671 m_fp = stderr;
672 else
673 m_fp = fp;
674 }
675
676 void wxLogStderr::DoLogText(const wxString& msg)
677 {
678 wxFputs(msg + '\n', m_fp);
679 fflush(m_fp);
680
681 // under GUI systems such as Windows or Mac, programs usually don't have
682 // stderr at all, so show the messages also somewhere else, typically in
683 // the debugger window so that they go at least somewhere instead of being
684 // simply lost
685 if ( m_fp == stderr )
686 {
687 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
688 if ( traits && !traits->HasStderr() )
689 {
690 wxMessageOutputDebug().Output(msg + wxS('\n'));
691 }
692 }
693 }
694
695 // ----------------------------------------------------------------------------
696 // wxLogStream implementation
697 // ----------------------------------------------------------------------------
698
699 #if wxUSE_STD_IOSTREAM
700 #include "wx/ioswrap.h"
701 wxLogStream::wxLogStream(wxSTD ostream *ostr)
702 {
703 if ( ostr == NULL )
704 m_ostr = &wxSTD cerr;
705 else
706 m_ostr = ostr;
707 }
708
709 void wxLogStream::DoLogText(const wxString& msg)
710 {
711 (*m_ostr) << msg << wxSTD endl;
712 }
713 #endif // wxUSE_STD_IOSTREAM
714
715 // ----------------------------------------------------------------------------
716 // wxLogChain
717 // ----------------------------------------------------------------------------
718
719 wxLogChain::wxLogChain(wxLog *logger)
720 {
721 m_bPassMessages = true;
722
723 m_logNew = logger;
724 m_logOld = wxLog::SetActiveTarget(this);
725 }
726
727 wxLogChain::~wxLogChain()
728 {
729 delete m_logOld;
730
731 if ( m_logNew != this )
732 delete m_logNew;
733 }
734
735 void wxLogChain::SetLog(wxLog *logger)
736 {
737 if ( m_logNew != this )
738 delete m_logNew;
739
740 m_logNew = logger;
741 }
742
743 void wxLogChain::Flush()
744 {
745 if ( m_logOld )
746 m_logOld->Flush();
747
748 // be careful to avoid infinite recursion
749 if ( m_logNew && m_logNew != this )
750 m_logNew->Flush();
751 }
752
753 void wxLogChain::DoLogRecord(wxLogLevel level,
754 const wxString& msg,
755 const wxLogRecordInfo& info)
756 {
757 // let the previous logger show it
758 if ( m_logOld && IsPassingMessages() )
759 m_logOld->LogRecord(level, msg, info);
760
761 // and also send it to the new one
762 if ( m_logNew && m_logNew != this )
763 m_logNew->LogRecord(level, msg, info);
764 }
765
766 #ifdef __VISUALC__
767 // "'this' : used in base member initializer list" - so what?
768 #pragma warning(disable:4355)
769 #endif // VC++
770
771 // ----------------------------------------------------------------------------
772 // wxLogInterposer
773 // ----------------------------------------------------------------------------
774
775 wxLogInterposer::wxLogInterposer()
776 : wxLogChain(this)
777 {
778 }
779
780 // ----------------------------------------------------------------------------
781 // wxLogInterposerTemp
782 // ----------------------------------------------------------------------------
783
784 wxLogInterposerTemp::wxLogInterposerTemp()
785 : wxLogChain(this)
786 {
787 DetachOldLog();
788 }
789
790 #ifdef __VISUALC__
791 #pragma warning(default:4355)
792 #endif // VC++
793
794 // ============================================================================
795 // Global functions/variables
796 // ============================================================================
797
798 // ----------------------------------------------------------------------------
799 // static variables
800 // ----------------------------------------------------------------------------
801
802 bool wxLog::ms_bRepetCounting = false;
803
804 wxLog *wxLog::ms_pLogger = NULL;
805 bool wxLog::ms_doLog = true;
806 bool wxLog::ms_bAutoCreate = true;
807 bool wxLog::ms_bVerbose = false;
808
809 wxLogLevel wxLog::ms_logLevel = wxLOG_Max; // log everything by default
810
811 size_t wxLog::ms_suspendCount = 0;
812
813 wxString wxLog::ms_timestamp(wxS("%X")); // time only, no date
814
815 #if WXWIN_COMPATIBILITY_2_8
816 wxTraceMask wxLog::ms_ulTraceMask = (wxTraceMask)0;
817 #endif // wxDEBUG_LEVEL
818
819 wxArrayString wxLog::ms_aTraceMasks;
820
821 // ----------------------------------------------------------------------------
822 // stdout error logging helper
823 // ----------------------------------------------------------------------------
824
825 // helper function: wraps the message and justifies it under given position
826 // (looks more pretty on the terminal). Also adds newline at the end.
827 //
828 // TODO this is now disabled until I find a portable way of determining the
829 // terminal window size (ok, I found it but does anybody really cares?)
830 #ifdef LOG_PRETTY_WRAP
831 static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
832 {
833 size_t nMax = 80; // FIXME
834 size_t nStart = strlen(pszPrefix);
835 fputs(pszPrefix, f);
836
837 size_t n;
838 while ( *psz != '\0' ) {
839 for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
840 putc(*psz++, f);
841
842 // wrapped?
843 if ( *psz != '\0' ) {
844 /*putc('\n', f);*/
845 for ( n = 0; n < nStart; n++ )
846 putc(' ', f);
847
848 // as we wrapped, squeeze all white space
849 while ( isspace(*psz) )
850 psz++;
851 }
852 }
853
854 putc('\n', f);
855 }
856 #endif //LOG_PRETTY_WRAP
857
858 // ----------------------------------------------------------------------------
859 // error code/error message retrieval functions
860 // ----------------------------------------------------------------------------
861
862 // get error code from syste
863 unsigned long wxSysErrorCode()
864 {
865 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
866 return ::GetLastError();
867 #else //Unix
868 return errno;
869 #endif //Win/Unix
870 }
871
872 // get error message from system
873 const wxChar *wxSysErrorMsg(unsigned long nErrCode)
874 {
875 if ( nErrCode == 0 )
876 nErrCode = wxSysErrorCode();
877
878 #if defined(__WXMSW__) && !defined(__WXMICROWIN__)
879 static wxChar s_szBuf[1024];
880
881 // get error message from system
882 LPVOID lpMsgBuf;
883 if ( ::FormatMessage
884 (
885 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
886 NULL,
887 nErrCode,
888 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
889 (LPTSTR)&lpMsgBuf,
890 0,
891 NULL
892 ) == 0 )
893 {
894 // if this happens, something is seriously wrong, so don't use _() here
895 // for safety
896 wxSprintf(s_szBuf, wxS("unknown error %lx"), nErrCode);
897 return s_szBuf;
898 }
899
900
901 // copy it to our buffer and free memory
902 // Crashes on SmartPhone (FIXME)
903 #if !defined(__SMARTPHONE__) /* of WinCE */
904 if( lpMsgBuf != 0 )
905 {
906 wxStrlcpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf));
907
908 LocalFree(lpMsgBuf);
909
910 // returned string is capitalized and ended with '\r\n' - bad
911 s_szBuf[0] = (wxChar)wxTolower(s_szBuf[0]);
912 size_t len = wxStrlen(s_szBuf);
913 if ( len > 0 ) {
914 // truncate string
915 if ( s_szBuf[len - 2] == wxS('\r') )
916 s_szBuf[len - 2] = wxS('\0');
917 }
918 }
919 else
920 #endif // !__SMARTPHONE__
921 {
922 s_szBuf[0] = wxS('\0');
923 }
924
925 return s_szBuf;
926 #else // !__WXMSW__
927 #if wxUSE_UNICODE
928 static wchar_t s_wzBuf[1024];
929 wxConvCurrent->MB2WC(s_wzBuf, strerror((int)nErrCode),
930 WXSIZEOF(s_wzBuf) - 1);
931 return s_wzBuf;
932 #else
933 return strerror((int)nErrCode);
934 #endif
935 #endif // __WXMSW__/!__WXMSW__
936 }
937
938 #endif // wxUSE_LOG