Imported Upstream version 1.4.99.2
[platform/upstream/syncevolution.git] / src / synthesis / src / sysync / debuglogger.cpp
1 /*
2  *  File:         debuglogger.cpp
3  *
4  *  Author:       Lukas Zeller (luz@plan44.ch)
5  *
6  *  Global debug mechanisms
7  *
8  *  Copyright (c) 2005-2011 by Synthesis AG + plan44.ch
9  *
10  *  2005-08-04 : luz : created
11  *
12  */
13
14
15 #include "prefix_file.h"
16
17 #ifdef SYDEBUG
18
19 #include "debuglogger.h"
20
21
22 #ifdef MULTI_THREAD_SUPPORT
23 #include "platform_thread.h"
24 #endif
25
26 #ifdef USE_DLT
27 #include <dlt.h>
28 #endif
29
30 namespace sysync {
31
32 #ifdef USE_DLT
33 static bool DbgDLTInitialized;
34 static DltContext DbgProtoContext;
35 static DltContext DbgSessionContext;
36 static DltContext DbgAdminContext;
37 static DltContext DbgDataContext;
38 static DltContext DbgRemoteInfoContext;
39 static DltContext DbgParseContext;
40 static DltContext DbgGenerateContext;
41 static DltContext DbgTranspContext;
42 static DltContext DbgSyncMLTKContext;
43 static DltContext DbgDefaultContext;
44 #endif
45
46
47 #ifndef HARDCODED_CONFIG
48
49 // debug format modes
50 cAppCharP const DbgOutFormatNames[numDbgOutFormats] = {
51   "text",       // plain text format (but can be indented)
52   "xml",        // XML format
53   "html"        // HTML format
54 #ifdef USE_DLT
55   // If DLT support is not enabled, then trying to uses it in a config
56   // will lead to a generic parse error. Might be good enough, although
57   // a dedicated error message about "DLT being disabled in this build"
58   // would be nicer.
59   , "dlt"         // GENIVI Diagnostic Log and Trace
60 #endif
61 };
62
63
64 // HTML dynamic folding modes
65 cAppCharP const DbgFoldingModeNames[numDbgFoldingModes] = {
66   "none",       // do not include dynamic folding into HTML logs
67   "collapsed",  // include folding - all collapsed by default
68   "expanded",   // include folding - all expanded by default
69   "auto"        // include folding - collapse/expand state predefined on a block-by-block basis
70 };
71
72
73 cAppCharP const DbgSourceModeNames[numDbgSourceModes] = {
74   "none",       // do not include links into source code in HTML logs
75   "hint",       // no links, but info about what file/line number the message comes from
76   "doxygen",    // include link into doxygen prepared HTML version of source code
77   "txmt",       // include txmt:// link (understood by TextMate and BBEdit) into source code
78 };
79
80
81 // debug flush modes
82 cAppCharP const DbgFlushModeNames[numDbgFlushModes] = {
83   "buffered",   // no flush, keep open as long as possible, output buffered (fast, needed for network drives)
84   "flush",      // flush every debug message
85   "openclose"   // open and close debug channel separately for every message (as in 2.x engine)
86 };
87
88 // debug subthread isolation modes
89 cAppCharP const DbgSubthreadModeNames[numDbgSubthreadModes] = {
90   "none",       // do not handle output from subthread specially
91   "suppress",   // suppress output from subthreads
92   "separate",   // create separate output stream (=file) for each subthread
93   "mix",        // mix on a line by line basis
94   "mixblocks"   // buffer thread's output and mix block-wise it into main stream when appropriate
95 };
96
97 #endif
98
99 // file extentsions for debug format modes
100 cAppCharP const DbgOutFormatExtensions[numDbgOutFormats] = {
101   ".log",        // plain text format (but can be indented)
102   ".xml",        // XML format
103   ".html"        // HTML format
104 };
105
106
107 cAppCharP const DbgOutDefaultPrefixes[numDbgOutFormats] = {
108   "*** Start of log",
109   "<?xml version=\"1.0\"?>\n"
110     "<sysync_log version=\"1.0\">",
111   "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
112   "<html><head><title>SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log</title>\n"
113     "<meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\">\n"
114     "<style type=\"text/css\" media=\"screen\"><!--\n"
115       ".block { color: #0000FF; font-weight: bold; }\n"
116       ".attribute { color: #A5002C; }\n"
117       ".attrval { color: #D80039; font-weight: bold; }\n"
118       ".error { color: red; font-weight: bold; }\n"
119       ".hotalone { color: #000000; font-weight: bold; }\n"
120       ".hot { font-weight: bold; }\n"
121       ".script { color: #996633; }\n" // brownish
122       ".source { color: #3333FF; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // keyword blue
123       ".comment { color: #669933; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // comment green
124       ".skipped { color: #BBBBBB; font-family:courier,monospace; font-size: 90%; font-weight: bold; }\n" // skipped code
125       ".value { color: #FF3300; }\n" // bright orange
126       ".filter { color: #997F66; }\n" // brownish pale
127       ".match { color: #A95E38; }\n" // brownish orange
128       ".dbapi { color: #CC3366; }\n" // dark reddish/pink (pink/violet = database)
129       ".plugin { color: #9151A3; }\n" // dark violet (pink/violet = database)
130       ".incoming { color: #196D00; }\n" // really dark green (green = remote)
131       ".outgoing { color: #002C84; }\n" // really dark blue (blue = local)
132       ".conflict { color: #990000; }\n" // dark red
133       ".remote { color: #709900; }\n" // greenish (green = remote)
134       ".proto { color: #777100; }\n" // dark yellowish/brown
135       ".rest { color: #AAAAAA; }\n" // greyed
136       ".exotic { color: #FF9900; }\n" // mango
137       "a.jump { color: #5D82BA; }\n"
138       "pre { font-size: 90%; }\n"
139       // for folding (always included, as it must be in header)
140       ".exp {\n"
141       " color: #FF0000;\n"
142       " font-weight: bold;\n"
143       " font-size: 90%;\n"
144       " width: 1em;\n"
145       " height: 1em;\n"
146       " display: inline;\n"
147       " border-width: 0.2em;\n"
148       " border-style: solid;\n"
149       " text-align: center;\n"
150       " vertical-align: middle;\n"
151       " padding: 0px 0.2em 0px 0.2em;\n"
152       " margin: 0 4px 2px 0;\n"
153       "}\n"
154       ".coll {\n"
155       " color: #754242;\n"
156       " font-weight: bold;\n"
157       " font-size: 90%;\n"
158       " width: 1em;\n"
159       " height: 1em;\n"
160       " display: inline;\n"
161       " border-width: 0.2em;\n"
162       " border-style: solid;\n"
163       " text-align: center;\n"
164       " vertical-align: middle;\n"
165       " padding: 0px 0.2em 0px 0.2em;\n"
166       " margin: 0 4px 2px 0;\n"
167       "}\n"
168       ".doall { color: #754242; }\n"
169     "--></style>\n"
170     "</head><body><h2>Start of log - SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING "</h2>\n<ul>\n"
171 };
172
173 cAppCharP const DbgOutDefaultSuffixes[numDbgOutFormats] = {
174   "*** End of log",
175   "</sysync_log>",
176   "</ul><h2>End of log</h2></html>"
177 };
178
179
180 cAppCharP FoldingPrefix =
181   "<script language=javascript1.2 type=text/javascript><!--\n"
182   "function div_ref_style (id) {\n"
183   " if      (document.layers)         return document.layers[id];\n"
184   " else if (document.all)            return document.all[id].style;\n"
185   " else if (document.getElementById) return document.getElementById(id).style;\n"
186   " else                              return null;\n"
187   "}\n"
188   "function exp(id) {\n"
189   " if(div_ref_style('B'+id).display!='block') {\n"
190   "  div_ref_style('B'+id).display='block';\n"
191   "  div_ref_style('E'+id).display='none';\n"
192   "  div_ref_style('C'+id).display='inline';\n"
193   " }\n"
194   "}\n"
195   "function coll(id) {\n"
196   " if(div_ref_style('B'+id).display!='none') {\n"
197   "  div_ref_style('B'+id).display='none';\n"
198   "  div_ref_style('E'+id).display='inline';\n"
199   "  div_ref_style('C'+id).display='none';\n"
200   " }\n"
201   "}\n"
202   "function doall(id,collapse) {\n"
203   " // get parent element\n"
204   " if (id=='') {\n"
205   "  mydiv=document;\n"
206   " }\n"
207   " else {\n"
208   "  mydiv=document.getElementById('B'+id); // get div to collapse or expand\n"
209   "  if (collapse) {\n"
210   "   coll(id);\n"
211   "  }\n"
212   "  else {\n"
213   "   exp(id);\n"
214   "  }\n"
215   " }\n"
216   " // get all contained blocks\n"
217   " divs=mydiv.getElementsByTagName('div')  // all divs\n"
218   " for (i=0 ; i<divs.length ; i++) {\n"
219   "  if (divs[i].className=='blk') {\n"
220   "   // this is a foldable block div\n"
221   "   bid = divs[i].id.substring(1);\n"
222   "   if (collapse) {\n"
223   "    coll(bid);\n"
224   "   }\n"
225   "   else {\n"
226   "    exp(bid);\n"
227   "   }\n"
228   "  }\n"
229   " }\n"
230   "}\n"
231   "--></script>\n"
232   "<li><span class=\"doall\" onclick=\"doall('',true)\">[-- collapse all --]</span><span class=\"doall\" onclick=\"doall('',false)\">[++ expand all ++]</span></li>\n";
233
234
235 // externals
236
237 #ifdef CONSOLEINFO
238 // privately redefined here to avoid circular headers (would need syncappbase.h)
239 extern "C" void ConsolePuts(const char *text);
240 #endif
241
242 // TDbgOptions implementation
243 // --------------------------
244
245 TDbgOptions::TDbgOptions()
246 {
247   // set defaults
248   clear();
249 } // TDbgOptions::TDbgOptions
250
251
252 void TDbgOptions::clear(void)
253 {
254   fOutputFormat = dbgfmt_html; // most universally readable and convenient
255   fIndentString = "  "; // two spaces
256   fCustomPrefix.erase(); // no custom prefix
257   fCustomSuffix.erase(); // no custom suffix
258   fSeparateMsgs = true; // separate text message lines (<msg></msg> in xml)
259   fTimestampStructure = true; // timestamps in structure...
260   fTimestampForAll = false; // ..but not for every line
261   fThreadIDForAll = false; // not by default
262   fFlushMode = dbgflush_none; // no special flush or openclose (fast, but might loose info on process abort)
263   fFoldingMode = dbgfold_auto; // dynamic folding enabled, expanded/collapsed defaults automatically set on block-by-block basis
264   #ifdef SYDEBUG_LOCATION
265   fSourceLinkMode = dbgsource_none; // no links into source code
266   fSourceRootPath = SYDEBUG_LOCATION; // use default path from build
267   #endif
268   fAppend = false; // default to overwrite existing logfiles
269   fSubThreadMode = dbgsubthread_suppress; // simply suppress subthread info
270   fSubThreadBufferMax = 1024*1024; // don't buffer more than one meg.
271 } // TDbgOptions::clear
272
273
274
275 // TDbgOut implementation
276 // ----------------------
277
278 TDbgOut::TDbgOut() :
279   fDestructed(false)
280 {
281   // init
282   fIsOpen=false;
283 } // TDbgOut::TDbgOut
284
285
286 TDbgOut::~TDbgOut()
287 {
288   destruct();
289 } // TDbgOut::~TDbgOut
290
291
292 void TDbgOut::destruct(void)
293 {
294   if (!fDestructed) doDestruct();
295   fDestructed=true;
296 } // TDbgOut::destruct
297
298
299 void TDbgOut::doDestruct(void)
300 {
301   // make sure files are closed
302   closeDbg();
303 } // TDbgOut::doDestruct
304
305
306 // TStdFileDbgOut implementation
307 // -----------------------------
308
309 #ifndef NO_C_FILES
310
311 TStdFileDbgOut::TStdFileDbgOut()
312 {
313   // init
314   fFileName.erase();
315   fFile=NULL;
316   mutex=newMutex();
317 } // TStdFileDbgOut::TStdFileDbgOut
318
319
320 TStdFileDbgOut::~TStdFileDbgOut()
321 {
322   destruct();
323   freeMutex(mutex);
324 } // TStdFileDbgOut::~TStdFileDbgOut
325
326
327 #ifdef LINUX
328 # include <unistd.h>
329 # include <fcntl.h>
330 #endif
331
332 static FILE *FOpen(const char *path, const char *mode)
333 {
334   FILE *file = fopen(path, mode);
335 #ifdef LINUX
336   // Be careful not to leak this file descriptor into forked
337   // processes.
338   int fd = file ? fileno(file) : -1;
339   if (fd >= 0) {
340     int flags = fcntl(fd, F_GETFD);
341     if (flags != -1) {
342       fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
343     }
344   }
345 #endif
346   return file;
347 }
348
349 // open standard C file based debug output channel
350 bool TStdFileDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
351 {
352   if (fIsOpen) {
353     // first close
354     closeDbg();
355   }
356   // now apply new flush mode
357   fFlushMode=aFlushMode;
358   // save new file name
359   fFileName=aDbgOutputName;
360   // for C files, use the extension provided
361   fFileName+=aSuggestedExtension;
362   // open
363   fFile=FOpen(fFileName.c_str(),aRawMode ? (aOverWrite ? "wb" : "ab") : (aOverWrite ? "w" : "a"));
364   // in case this fails, we'll have a NULL fFile. We can't do anything more here
365   fIsOpen=fFile!=NULL;
366   // For openclose mode, we have opened here only to check for logfile writability - close again
367   if (fIsOpen && fFlushMode==dbgflush_openclose) {
368     fclose(fFile);
369     fFile=NULL;
370   }
371   // return false if we haven't been successful opening the channel
372   return fIsOpen;
373 } // TStdFileDbgOut::openDbg
374
375
376 // return current size of debug file
377 uInt32 TStdFileDbgOut::dbgFileSize(void)
378 {
379   if (!fIsOpen) return 0; // no file, no size
380   uInt32 sz;
381   if (fFlushMode==dbgflush_openclose) {
382     // we need to open the file for append first
383     fFile=FOpen(fFileName.c_str(),"a");
384     fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
385     sz=ftell(fFile);
386     fclose(fFile);
387     fFile=NULL;
388   }
389   else {
390     fseek(fFile,0,SEEK_END); // move to end (needed, otherwise ftell may return 0 despite "a" fopen mode)
391     sz=ftell(fFile); // return size
392   }
393   return sz;
394 } // TStdFileDbgOut::dbgFileSize
395
396
397 // close standard C file based debug channel
398 void TStdFileDbgOut::closeDbg(void)
399 {
400   if (fIsOpen) {
401     if (fFile) {
402       fclose(fFile);
403       fFile=NULL;
404     }
405     fIsOpen=false;
406   }
407 } // TStdFileDbgOut::closeDbg
408
409
410 // write single line to standard file based output channel
411 void TStdFileDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
412 {
413   // if not open, just NOP
414   if (fIsOpen) {
415     if (fFlushMode==dbgflush_openclose) {
416       // we need to open the file for append first
417       lockMutex(mutex);
418       fFile=FOpen(fFileName.c_str(),"a");
419       if (!fFile)
420         unlockMutex(mutex);
421     }
422     if (fFile) {
423       // now output
424       fputs(aLine,fFile);
425       fputs("\n",fFile);
426
427       // do required flushing
428       if (fFlushMode==dbgflush_openclose) {
429         // we need to close the file after every line of output
430         fclose(fFile);
431         fFile=NULL;
432         unlockMutex(mutex);
433       }
434       else if (aForceFlush || fFlushMode==dbgflush_flush) {
435         // simply flush
436         fflush(fFile);
437       }
438     }
439   }
440 } // TStdFileDbgOut::putLine
441
442
443 // write raw data to output file
444 void TStdFileDbgOut::putRawData(cAppPointer aData, memSize aSize)
445 {
446   if (fIsOpen) {
447     if (fFlushMode==dbgflush_openclose) {
448       // we need to open the file for append first
449       lockMutex(mutex);
450       fFile=FOpen(fFileName.c_str(),"a");
451     }
452     if (fFile) {
453       if (fwrite(aData, 1, aSize, fFile) != 1) {
454         // error ignored
455       }
456     }
457     // do required flushing
458     if (fFlushMode==dbgflush_openclose) {
459       // we need to close the file after every line of output
460       fclose(fFile);
461       fFile=NULL;
462       unlockMutex(mutex);
463     }
464     else if (fFlushMode==dbgflush_flush) {
465       // simply flush
466       fflush(fFile);
467     }
468   }
469 } // TStdFileDbgOut::putRawData
470
471
472 #endif
473
474
475 // TConsoleDbgOut implementation
476 // -----------------------------
477
478 TConsoleDbgOut::TConsoleDbgOut()
479 {
480   // init
481 } // TStdFileDbgOut::TStdFileDbgOut
482
483
484 // open standard C file based debug output channel
485 bool TConsoleDbgOut::openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode)
486 {
487   if (fIsOpen) {
488     // first close
489     closeDbg();
490   }
491   // raw mode is not supported
492   if (!aRawMode)
493     fIsOpen=true;
494   // return false if we haven't been successful opening the channel
495   return fIsOpen;
496 } // TConsoleDbgOut::openDbg
497
498
499 // close standard C file based debug channel
500 void TConsoleDbgOut::closeDbg(void)
501 {
502   fIsOpen=false;
503 } // TConsoleDbgOut::closeDbg
504
505
506 // write single line to standard file based output channel
507 void TConsoleDbgOut::putLine(cAppCharP aLine, bool aForceFlush)
508 {
509   // if not open, just NOP
510   if (fIsOpen) {
511     CONSOLEPUTS(aLine);
512   }
513 } // TConsoleDbgOut::putLine
514
515
516
517
518 // TDebugLoggerBase implementation
519 // -------------------------------
520
521 // constructor
522 TDebugLoggerBase::TDebugLoggerBase(GZones *aGZonesP) :
523   fGZonesP(aGZonesP)
524 {
525   fDebugMask=0;
526   fDebugEnabled=true; // enabled by default
527   fNextDebugMask=0;
528   fDbgOutP=NULL;
529   fDbgOptionsP=NULL;
530   fIndent=0;
531   fBlockHistory=NULL; // no Block open yet
532   fOutStarted=false; // not yet started
533   fBlockNo=0;
534   fGZonesP=NULL;
535   fOutputLoggerP=NULL; // no redirected output yet
536 } // TDebugLoggerBase::TDebugLoggerBase
537
538
539 // destructor
540 TDebugLoggerBase::~TDebugLoggerBase()
541 {
542   // make sure debug is finalized
543   DebugFinalizeOutput();
544   // make sure possibly left-over history elements are erased
545   while (fBlockHistory) {
546     TBlockLevel *bl=fBlockHistory;
547     fBlockHistory=bl->fNext;
548     delete bl;
549   }
550   // get rid of output object
551   if (fDbgOutP) delete fDbgOutP;
552   fDbgOutP=NULL;
553 } // TDebugLoggerBase::TDebugLoggerBase
554
555
556 // @brief convenience version for getting time
557 lineartime_t TDebugLoggerBase::getSystemNowAs(timecontext_t aContext)
558 {
559   return sysync::getSystemNowAs(aContext,fGZonesP);
560 } // TDebugLoggerBase::getSystemNowAs
561
562
563 // install outputter
564 void TDebugLoggerBase::installOutput(TDbgOut *aDbgOutP)
565 {
566   // get rid of possibly installed previous outputter
567   if (fDbgOutP) delete fDbgOutP;
568   fDbgOutP=aDbgOutP;
569 } // TDebugLoggerBase::installOutput
570
571
572 /// @brief link this logger to another logger and redirect output to that logger
573 /// @param aDebugLoggerP[in] another logger, that must be alive as long as this logger is alive
574 void TDebugLoggerBase::outputVia(TDebugLoggerBase *aDebugLoggerP)
575 {
576   // save logger and prefix
577   fOutputLoggerP = aDebugLoggerP;
578 } // TDebugLoggerBase::outputVia
579
580 #if defined(CONSOLEINFO) && defined(CONSOLEINFO_LIBC)
581 extern "C" {
582   int (*SySync_ConsolePrintf)(FILE *stream, const char *format, ...) = fprintf;
583 }
584 #endif
585
586 // output formatted text
587 void TDebugLoggerBase::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
588 {
589   // we need a format and debug not completely off
590   if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
591     const sInt16 maxmsglen=1024;
592     char msg[maxmsglen];
593     msg[0]='\0';
594     // assemble the message string
595     vsnprintf(msg, maxmsglen, aFormat, aArgs);
596     // write the string
597     DebugPuts(TDBG_LOCATION_ARG aDbgMask,msg);
598   }
599 } // TDebugLoggerBase::DebugVPrintf
600
601
602 // helper needed for maintaining old DEBUGPRINTFX() macro syntax
603 TDebugLoggerBase &TDebugLoggerBase::setNextMask(uInt32 aDbgMask)
604 {
605   fNextDebugMask=aDbgMask;
606   return *this;
607 } // TDebugLoggerBase::setNextMask
608
609
610 // like DebugPrintf(), but using mask previously set by setNextMask()
611 void TDebugLoggerBase::DebugPrintfLastMask(TDBG_LOCATION_PROTO cAppCharP aFormat, ...)
612 {
613   va_list args;
614   // we need a format and debug not completely off
615   if ((getMask() & fNextDebugMask)==fNextDebugMask && aFormat) {
616     va_start(args, aFormat);
617     DebugVPrintf(TDBG_LOCATION_ARG fNextDebugMask,aFormat,args);
618     va_end(args);
619   }
620   fNextDebugMask=0;
621 } // TDebugLoggerBase::DebugPrintfLastMask
622
623
624 // output formatted text
625 void TDebugLoggerBase::DebugPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, ...)
626 {
627   va_list args;
628   // we need a format and debug not completely off
629   if ((getMask() & aDbgMask)==aDbgMask && aFormat) {
630     va_start(args, aFormat);
631     DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,args);
632     va_end(args);
633   }
634 } // TDebugLoggerBase::DebugVPrintf
635
636
637 // open new Block without attribute list
638 void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed)
639 {
640   // we need a format and debug not completely off
641   if (getMask() && aBlockName) {
642 #ifdef __clang__
643     #pragma clang diagnostic push
644     #pragma clang diagnostic ignored "-Wformat-security"
645 #endif
646     DebugOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,NULL);
647 #ifdef __clang__
648     #pragma clang diagnostic pop
649 #endif
650   }
651 } // TDebugLoggerBase::DebugOpenBlock
652
653
654 // open new Block with attribute list, printf style
655 void TDebugLoggerBase::DebugOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
656 {
657   va_list args;
658   // we need a format and debug not completely off
659   if (getMask() && aBlockName) {
660     va_start(args, aBlockFmt);
661     DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,aCollapsed,aBlockFmt,args);
662     va_end(args);
663   }
664 } // TDebugLoggerBase::DebugOpenBlock
665
666
667 // open new Block with attribute list, printf style, expanded by default
668 void TDebugLoggerBase::DebugOpenBlockExpanded(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
669 {
670   va_list args;
671   // we need a format and debug not completely off
672   if (getMask() && aBlockName) {
673     va_start(args, aBlockFmt);
674     DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,false,aBlockFmt,args);
675     va_end(args);
676   }
677 } // TDebugLoggerBase::DebugOpenBlockExpanded
678
679
680 // open new Block with attribute list, printf style, collapsed by default
681 void TDebugLoggerBase::DebugOpenBlockCollapsed(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
682 {
683   va_list args;
684   // we need a format and debug not completely off
685   if (getMask() && aBlockName) {
686     va_start(args, aBlockFmt);
687     DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName,aBlockTitle,true,aBlockFmt,args);
688     va_end(args);
689   }
690 } // TDebugLoggerBase::DebugOpenBlockCollapsed
691
692
693 #ifdef SYDEBUG_LOCATION
694
695 #define MAKEDBGLINK(txt) dbg2Link(TDBG_LOCATION_ARG txt)
696
697 /// turn text into link to source code
698 string TDebugLoggerBase::dbg2Link(const TDbgLocation &aTDbgLoc, const string &aTxt)
699 {
700   if (!aTDbgLoc.fFile || !fDbgOptionsP || fDbgOptionsP->fSourceLinkMode==dbgsource_none || fDbgOptionsP->fOutputFormat!=dbgfmt_html)
701     return aTxt; // disabled, non-html or no information to create source link
702   // create link or hint to source code
703   string line;
704
705   switch(fDbgOptionsP->fSourceLinkMode) {
706     case dbgsource_hint: {
707       // only add name/line number/function as title hint (in a otherwise inactive link)
708       line = "<a href=\"#\" title=";
709       StringObjPrintf(line,"<a href=\"#\" title=\"%s:%d",aTDbgLoc.fFile,aTDbgLoc.fLine);
710       StringObjAppendPrintf(line," in %s",aTDbgLoc.fFunction);
711       line += '"';
712       goto closelink;
713     }
714     case dbgsource_doxygen: {
715       // create link into doxygen
716       line = "<a href=\"";
717       // replace path with path to Doxygen HTML pages,
718       // mangle base name like Doxygen does
719       line += fDbgOptionsP->fSourceRootPath;
720       line += "/";
721       string file = aTDbgLoc.fFile;
722       size_t off = file.rfind('/');
723       if (off != file.npos)
724         file = file.substr(off + 1);
725       for (off = 0; off < file.size(); off++) {
726         switch(file[off]) {
727         case '_':
728           line+="__";
729           break;
730         case '.':
731           line+="_8";
732           break;
733         default:
734           line+=file[off];
735           break;
736         }
737       }
738       StringObjAppendPrintf(line,"-source.html#l%05d",aTDbgLoc.fLine);
739       line+="\"";
740       if (aTDbgLoc.fFunction) {
741         line+=" title=\"";
742         line+=aTDbgLoc.fFunction;
743         line+="\"";
744       }
745       goto closelink;
746     }
747     case dbgsource_txmt: {
748       // create txmt:// URL scheme link, which opens TextMate or BBEdit at the correct line in MacOS X
749       line = "<a href=\"txmt://open/?url=file://";
750       // - create path
751       string path = fDbgOptionsP->fSourceRootPath;
752       path += aTDbgLoc.fFile;
753       // - add path CGI encoded
754       line += encodeForCGI(path.c_str());
755       // - add line number
756       if (aTDbgLoc.fLine>0)
757         StringObjAppendPrintf(line,"&line=%d",aTDbgLoc.fLine);
758       line+="\"";
759       if (aTDbgLoc.fFunction) {
760         line+=" title=\"";
761         line+=aTDbgLoc.fFunction;
762         line+='"';
763       }
764     }
765     closelink: {
766       line+=">";
767       line+=aTxt;
768       line+="</a>";
769       break;
770     }
771     default:
772       line = aTxt;
773   } // switch
774   // return
775   return line;
776 } // TDebugLoggerBase::dbg2Link
777
778 #else
779
780 #define MAKEDBGLINK(txt) (txt)
781
782 #endif // SYDEBUG_LOCATION
783
784 #ifdef USE_DLT
785 static void DbgText2PlainText(const char *in, size_t len, std::string &out)
786 {
787   const char *q=in;
788   const char *s=q;
789   const char *end=in + len;
790
791   while (q<end) {
792     if (*q=='&') {
793       if (end-q>=6 && strucmp(q,"&html;",6)==0) {
794         if (q>s) out.append(s,q-s);
795         // everything until next &html; must be filtered out
796         // - search next &html;
797         s=q=q+6;
798         while(q+6<=end && strucmp(q,"&html;",6)!=0) q++;
799         s=q=q+6;
800       }
801       else if (end-q>=4 && strucmp(q,"&sp;",4)==0) {
802         if (q>s) out.append(s,q-s);
803         s=q=q+4;
804         out += ' '; // convert to plain space
805       }
806       else
807         q++;
808     }
809     else {
810       q++;
811     }
812   }
813   if (q>s) out.append(s,q-s);
814 }
815 #endif // USE_DLT
816
817
818 // output text to debug channel
819 void TDebugLoggerBase::DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
820 {
821   // we need a text and debug not completely off
822   if (!((getMask() & aDbgMask)==aDbgMask && aText && fDbgOptionsP)) {
823     // cannot output
824     //#ifdef __MWERKS__
825     //#warning "ugly hack"
826     DebugPutLine(TDBG_LOCATION_NONE "<li><span class=\"error\">Warning: Dbg output system already half shut down (limited formatting)!</span></li><li>");
827     if (aText) DebugPutLine(TDBG_LOCATION_NONE aText);
828     DebugPutLine(TDBG_LOCATION_NONE "</li>");
829     //#endif
830   }
831   else {
832     // make sure output is started
833     if (!fOutStarted) {
834       // try starting output
835       DebugStartOutput();
836       // disable debugging in this logger if starting output failed
837       // (prevents endless re-trying to open debug logs e.g. when log directory does not exist)
838       if (!fOutStarted) {
839         fDebugEnabled = false;
840         return; // stop all efforts here
841       }
842     }
843
844 #ifdef USE_DLT
845     // DLT logging logs everything in one chunk
846     if (fDbgOptionsP->fOutputFormat == dbgfmt_dlt) {
847       DltContext *context = &DbgDefaultContext;
848       if (aDbgMask & DBG_PROTO) {
849         context = &DbgProtoContext;
850       }
851       else if (aDbgMask & DBG_SESSION) {
852         context = &DbgSessionContext;
853       }
854       else if (aDbgMask & DBG_ADMIN) {
855         context = &DbgAdminContext;
856       }
857       else if (aDbgMask & DBG_DATA) {
858         context = &DbgDataContext;
859       }
860       else if (aDbgMask & DBG_REMOTEINFO) {
861         context = &DbgRemoteInfoContext;
862       }
863       else if (aDbgMask & DBG_PARSE) {
864         context = &DbgParseContext;
865       }
866       else if (aDbgMask & DBG_GEN) {
867         context = &DbgGenerateContext;
868       }
869       else if (aDbgMask & DBG_TRANSP) {
870         context = &DbgTranspContext;
871       }
872       else if (aDbgMask & (DBG_RTK_SML|DBG_RTK_XPT)) {
873         context = &DbgSyncMLTKContext;
874       }
875
876       DltLogLevelType level = DLT_LOG_VERBOSE;
877       if (level > DLT_LOG_INFO &&
878           (aDbgMask & DBG_HOT)) {
879         level = DLT_LOG_INFO;
880       }
881       if (level > DLT_LOG_ERROR &&
882           (aDbgMask & DBG_ERROR)) {
883         level = DLT_LOG_ERROR;
884       }
885       if (level > DLT_LOG_DEBUG &&
886           (aDbgMask & (DBG_USERDATA|DBG_PLUGIN|DBG_FILTER|DBG_CONFLICT))) {
887         level = DLT_LOG_DEBUG;
888       }
889       if (level < DLT_LOG_VERBOSE &&
890           (aDbgMask & DBG_DETAILS)) {
891         level = (DltLogLevelType)((int)level + 1);
892       }
893       if ((aTextSize > 0 && strlen(aText) > aTextSize) ||
894           strstr(aText, "&html;") ||
895           strstr(aText, "&sp;")) {
896             // Must make a copy and potentially filter out html markup.
897         string buffer;
898         buffer.reserve(aTextSize);
899         DbgText2PlainText(aText, aTextSize ? aTextSize : strlen(aText), buffer);
900         DLT_LOG(*context, level, DLT_STRING(buffer.c_str()));
901       } else {
902         // Fast path: log directly.
903         DLT_LOG(*context, level, DLT_STRING(aText));
904       }
905       return;
906     }
907 #endif // USE_DLT
908
909     // dissect into lines
910     cAppCharP end=aTextSize ? aText+aTextSize : NULL;
911     bool firstLine=true;
912     // check for preformatted message
913     bool pre=strnncmp(aText,"&pre;",5)==0;
914     if (pre) aText+=5;
915     pre = pre || aPreFormatted;
916     // now process text
917     while ((!end || aText<end) && *aText) {
918       // search for line end or end of string
919       cAppCharP p=aText;
920       while ((!end || p<end) && *p && *p!='\n' && *p!='\r') p++;
921       // output this line, properly formatted
922       string line;
923       line.erase();
924       cAppCharP q,s;
925       string ts;
926       switch (fDbgOptionsP->fOutputFormat) {
927         // HTML
928         case dbgfmt_html:
929           // prefix first line with <li>, second and further with <br/>
930           if (firstLine) {
931             line="<li>";
932             // add timestamp if needed for every line
933             if (
934               fDbgOptionsP->fTimestampForAll
935               || fDbgOptionsP->fThreadIDForAll
936               #ifdef SYDEBUG_LOCATION
937               || fDbgOptionsP->fSourceLinkMode!=dbgsource_none
938               #endif
939             ) {
940               string prefix;
941               prefix = "<i>[";
942               #ifdef MULTI_THREAD_SUPPORT
943               if (fDbgOptionsP->fThreadIDForAll) {
944                 StringObjAppendPrintf(prefix,"%09lu",myThreadID());
945                 if (fDbgOptionsP->fTimestampForAll) prefix += ", ";
946               }
947               #endif
948               if (fDbgOptionsP->fTimestampForAll) {
949                 StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
950                 prefix += ts;
951               }
952               #ifdef SYDEBUG_LOCATION
953               else if (!fDbgOptionsP->fThreadIDForAll) {
954                 // neither threadID nor timestamp, but source requested -> put small text here
955                 prefix += "src";
956               }
957               #endif
958               prefix+="]</i>&nbsp;";
959               // if we have links into source code, add it here
960               line += MAKEDBGLINK(prefix);
961             }
962             // colorize some messages
963             string cl="";
964             // colors, not mixable, most relevant first
965             if (aDbgMask & DBG_ERROR) {
966               cl="error";
967             }
968             else if (aDbgMask & DBG_EXOTIC) {
969               cl="exotic";
970             }
971             else if (aDbgMask & DBG_SCRIPTS) {
972               cl="script";
973             }
974             else if (aDbgMask & DBG_PLUGIN) {
975               cl="plugin";
976             }
977             else if (aDbgMask & DBG_DBAPI) {
978               cl="dbapi";
979             }
980             else if (aDbgMask & DBG_CONFLICT) {
981               cl="conflict";
982             }
983             else if (aDbgMask & DBG_MATCH) {
984               cl="match";
985             }
986             else if (aDbgMask & DBG_REMOTEINFO) {
987               cl="remote";
988             }
989             else if (aDbgMask & DBG_PROTO) {
990               cl="proto";
991             }
992             else if (aDbgMask & DBG_FILTER) {
993               cl="filter";
994             }
995             else if (aDbgMask & DBG_PARSE) {
996               cl="incoming";
997             }
998             else if (aDbgMask & DBG_GEN) {
999               cl="outgoing";
1000             }
1001             else if (aDbgMask & DBG_REST) {
1002               cl="rest";
1003             }
1004             // apply basic color style
1005             if (!cl.empty()) {
1006               line+="<span class=\""; line+=cl; line+="\">";
1007             }
1008             // aditional style modifiers that can be combined with colors
1009             if (aDbgMask & DBG_HOT) {
1010               if (cl.empty())
1011                 line+="<span class=\"hotalone\">";
1012               else
1013                 line+="<span class=\"hot\">";
1014             }
1015             // start preformatted if selected
1016             if (pre)
1017               line+="<pre>";
1018           }
1019           else {
1020             if (!pre) line="<br/>";
1021           }
1022           goto xmlize;
1023         // XML, just output and replace special chars as needed
1024         case dbgfmt_xml:
1025           if (firstLine) {
1026             #ifdef MULTI_THREAD_SUPPORT
1027             if (fDbgOptionsP->fThreadIDForAll) {
1028               line+="<thread>";
1029               StringObjAppendPrintf(line,"%09lu",myThreadID());
1030               line+="</thread>";
1031             }
1032             #endif
1033             if (fDbgOptionsP->fTimestampForAll) {
1034               StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1035               line+="<time>";
1036               line+=ts;
1037               line+="</time>";
1038             }
1039             DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size());
1040             line.erase();
1041           }
1042           if (fDbgOptionsP->fSeparateMsgs) {
1043             line+="<msg>";
1044           }
1045         xmlize:
1046           q=aText;
1047           s=q;
1048           while (q<p) {
1049             if (*q=='&') {
1050               if (strucmp(q,"&html;",6)==0) {
1051                 if (q>s) line.append(s,q-s); // flush stuff scanned so far
1052                 // everything until next &html; does not need or want escaping, copy it as is
1053                 // - search next &html;
1054                 s=q=q+6;
1055                 while(*q && strucmp(q,"&html;",6)!=0) q++;
1056                 // - append everything between if we are in HTML mode
1057                 if (fDbgOptionsP->fOutputFormat==dbgfmt_html && q>s)
1058                   line.append(s,q-s);
1059                 s=q=q+6;
1060               }
1061               else if (strucmp(q,"&sp;",4)==0) {
1062                 if (q>s) line.append(s,q-s); // flush stuff scanned so far
1063                 // non-breaking space in HTML, normal space otherwise
1064                 if (fDbgOptionsP->fOutputFormat==dbgfmt_html)
1065                   line += "&nbsp;";
1066                 else
1067                   line += ' ';
1068                 s=q=q+4; // skip &sp;
1069               }
1070               else {
1071                 if (q>s) line.append(s,q-s);
1072                 line+="&amp;";
1073                 s=++q;
1074               }
1075             }
1076             else if (*q=='<') {
1077               if (q>s) line.append(s,q-s);
1078               line+="&lt;";
1079               s=++q;
1080             }
1081             else if (*q=='>') {
1082               if (q>s) line.append(s,q-s);
1083               line+="&gt;";
1084               s=++q;
1085             }
1086             else {
1087               q++;
1088             }
1089           }
1090           if (q>s) line.append(s,q-s);
1091           if (fDbgOptionsP->fSeparateMsgs && fDbgOptionsP->fOutputFormat==dbgfmt_xml) {
1092             line+="</msg>";
1093           }
1094           break;
1095         // plain text
1096         default:
1097         case dbgfmt_text:
1098           q=aText;
1099           s=q;
1100           while (q<p) {
1101             if (*q=='&') {
1102               if (strucmp(q,"&html;",6)==0) {
1103                 if (q>s) line.append(s,q-s);
1104                 // everything until next &html; must be filtered out
1105                 // - search next &html;
1106                 s=q=q+6;
1107                 while(*q && strucmp(q,"&html;",6)!=0) q++;
1108                 s=q=q+6;
1109               }
1110               else if (strucmp(q,"&sp;",4)==0) {
1111                 if (q>s) line.append(s,q-s);
1112                 s=q=q+4;
1113                 line += ' '; // convert to plain space
1114               }
1115               else
1116                 q++;
1117             }
1118             else {
1119               q++;
1120             }
1121           }
1122           if (q>s) line.append(s,q-s);
1123           break;
1124       } // switch text output
1125       firstLine=false;
1126       // skip the lineend, if any
1127       while (((!end || p<end) && *p=='\n') || *p=='\r') p++;
1128       if (fDbgOptionsP->fOutputFormat==dbgfmt_html && ((end && p>=end) || *p==0)) {
1129         // end preformatted
1130         if (pre)
1131           line+="</pre>";
1132         // colors
1133         if (aDbgMask & (
1134           DBG_ERROR |
1135           DBG_SCRIPTS |
1136           DBG_REST |
1137           DBG_EXOTIC |
1138           DBG_DBAPI |
1139           DBG_PLUGIN |
1140           DBG_PARSE |
1141           DBG_GEN |
1142           DBG_CONFLICT |
1143           DBG_MATCH |
1144           DBG_REMOTEINFO |
1145           DBG_PROTO |
1146           DBG_FILTER
1147         )) {
1148           line+="</span>"; // end special style
1149         }
1150         // HOT modifier
1151         if (aDbgMask & (
1152           DBG_HOT
1153         )) {
1154           line+="</span>"; // end special style
1155         }
1156         line+="</li>"; // we need to close the list entry
1157       }
1158       DebugPutLine(TDBG_LOCATION_NONE line.c_str(),line.size(),pre);
1159       // next line, if any
1160       aText=p;
1161     } // loop until all text done
1162   }
1163 } // TDebugLoggerBase::DebugPuts
1164
1165
1166 // open new Block with attribute list, varargs passed
1167 void TDebugLoggerBase::DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
1168 {
1169   if (!fDbgOptionsP)
1170     return;
1171   if (fDbgOptionsP->fFoldingMode==dbgfold_collapsed)
1172     aCollapsed=true;
1173   else if (fDbgOptionsP->fFoldingMode==dbgfold_expanded)
1174     aCollapsed=false;
1175   if (getMask() && aBlockName && fDbgOptionsP) {
1176     // make sure output is started
1177     if (!fOutStarted) DebugStartOutput();
1178     // create Block line on current indent level
1179     string bl;
1180     string ts;
1181     // - preamble, possibly with timestamp
1182     bool withTime = fDbgOptionsP->fTimestampStructure;
1183     if (withTime)
1184       StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1185     switch (fDbgOptionsP->fOutputFormat) {
1186       // XML
1187       case dbgfmt_xml:
1188         bl="<"; bl+=aBlockName;
1189         if (withTime) {
1190           bl+=" time=\"" + ts + "\"";
1191         }
1192         if (aBlockTitle) {
1193           bl+=" title=\"";
1194           bl+=aBlockTitle;
1195           bl+="\"";
1196         }
1197         break;
1198       // HTML
1199       case dbgfmt_html:
1200         bl="<li><span class=\"block\">";
1201         if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1202           StringObjAppendPrintf(bl,
1203             "<div id=\"E%ld\" style=\"display:%s\" class=\"exp\" onclick=\"exp('%ld')\">+</div><div id=\"C%ld\" style=\"display:%s\" class=\"coll\" onclick=\"coll('%ld')\">&ndash;</div>",
1204                                 long(getBlockNo()), aCollapsed ? "inline" : "none", long(getBlockNo()),
1205                                 long(getBlockNo()), aCollapsed ? "none" : "inline", long(getBlockNo())
1206           );
1207         }
1208         StringObjAppendPrintf(bl,"<a name=\"H%ld\">", long(getBlockNo()));
1209         if (withTime) {
1210           bl += MAKEDBGLINK(string("[") + ts + "] ");
1211         }
1212         #ifdef SYDEBUG_LOCATION
1213         else if (fDbgOptionsP->fSourceLinkMode!=dbgsource_none) {
1214           bl += MAKEDBGLINK(string("[src] "));
1215         }
1216         #endif
1217         bl+="'";
1218         bl+=aBlockName;
1219         bl+="'";
1220         if (aBlockTitle) {
1221           bl+=" - ";
1222           bl+=aBlockTitle;
1223         }
1224         bl+="</a></span><span class=\"attribute\">";
1225         break;
1226       // plain text
1227       default:
1228       case dbgfmt_text:
1229         bl.erase();
1230         if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
1231           bl+="[" + ts + "] ";
1232         }
1233         bl+=aBlockName;
1234         if (aBlockTitle) {
1235           bl+=" - ";
1236           bl+=aBlockTitle;
1237         }
1238         break;
1239     } // switch preamble
1240     // - attributes
1241     if (aBlockFmt) {
1242       // first expand all printf parameters
1243       string attrs;
1244       vStringObjPrintf(attrs,aBlockFmt,true,aArgs);
1245       // isolate |-separated attribute format strings
1246       cAppCharP q,r,s,p=attrs.c_str();
1247       while (*p) {
1248         // search for beginning of value
1249         q=p;
1250         while(*q && *q!='=' && *q!='|') q++;
1251         // search for end of value
1252         r=q;
1253         s=q; // in case we don't have a =
1254         if (*q=='=') {
1255           s=q+1;
1256           r=s;
1257           while (*r && *r!='|') r++;
1258         }
1259         // now: p=start of attrname, q=end of attrname
1260         //      s=start of value, r=end of value
1261         // output an attribute now
1262         if (q>p && r>s) {
1263           switch (fDbgOptionsP->fOutputFormat) {
1264             // XML
1265             case dbgfmt_xml:
1266               bl+=" ";
1267               bl.append(p,q-p);
1268               bl+="=\"";
1269               bl.append(s,r-s);
1270               bl+="\"";
1271               break;
1272             case dbgfmt_html:
1273               bl+=", ";
1274               bl.append(p,q-p);
1275               bl+="=<span class=\"attrval\">";
1276               bl.append(s,r-s);
1277               bl+="</span>";
1278               break;
1279             case dbgfmt_text:
1280             default:
1281               bl+=", ";
1282               bl.append(p,q-p);
1283               bl+="=";
1284               bl.append(s,r-s);
1285               break;
1286           } // switch attribute
1287         } // non-empty attribute
1288         // more attributes to come?
1289         if (*r=='|') r++; // skip separator
1290         p=r;
1291       } // while
1292     } // attributes present
1293     // - finalize Block
1294     switch (fDbgOptionsP->fOutputFormat) {
1295       // XML
1296       case dbgfmt_xml:
1297         bl+=">";
1298         break;
1299       // HTML
1300       case dbgfmt_html:
1301         bl+="</span>"; // end span for attributes
1302         if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1303           StringObjAppendPrintf(bl,
1304             "&nbsp;<span class=\"doall\" onclick=\"doall('%ld',true)\">[--]</span><span class=\"doall\" onclick=\"doall('%ld',false)\">[++]</span>",
1305                                 long(getBlockNo()), long(getBlockNo())
1306           );
1307         }
1308         // link to end of block
1309         StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#F%ld\">[->end]</a>", long(getBlockNo()));
1310         // link to start of enclosing block (if any)
1311         if (fBlockHistory) {
1312           StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#H%ld\">[->enclosing]</a>", long(fBlockHistory->fBlockNo));
1313         }
1314         // start div for content folding
1315         if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1316           StringObjAppendPrintf(bl,
1317             "<div class=\"blk\" id=\"B%ld\" style=\"display:%s\">",
1318             long(getBlockNo()),
1319             aCollapsed ? "none" : "inline"
1320           );
1321         }
1322         bl+="<ul>"; // now start list for block's contents
1323         break;
1324       // plain text
1325       default:
1326       case dbgfmt_text:
1327         break;
1328     } // switch preamble
1329     // now output Block line (on current indent level)
1330     DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size());
1331     // increase indent level (applies to all Block contents)
1332     fIndent++;
1333     // save Block on stack
1334     TBlockLevel *newLevel = new TBlockLevel;
1335     newLevel->fBlockName=aBlockName;
1336     newLevel->fNext=fBlockHistory;
1337     newLevel->fBlockNo=getBlockNo(); // save block number to reference block in collapse box at end of block
1338     nextBlock(); // increment block number
1339     fBlockHistory=newLevel; // insert new level at start of list
1340   }
1341 } // TDebugLoggerBase::DebugVOpenBlock
1342
1343
1344 // close named Block. If no name given, topmost Block will be closed
1345 void TDebugLoggerBase::DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName)
1346 {
1347   if (fOutStarted && getMask() && fDbgOptionsP && fBlockHistory) {
1348     if (aBlockName==NULL) {
1349       #if SYDEBUG>1
1350       internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),"Block Nest Warning: Missing Block name at close");
1351       #else
1352       internalCloseBlocks(TDBG_LOCATION_ARG fBlockHistory->fBlockName.c_str(),NULL);
1353       #endif
1354     }
1355     else {
1356       internalCloseBlocks(TDBG_LOCATION_ARG aBlockName,NULL);
1357     }
1358   }
1359 } // TDebugLoggerBase::DebugCloseBlock
1360
1361
1362
1363 // internal helper used to close all or some Blocks
1364 void TDebugLoggerBase::internalCloseBlocks(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aCloseComment)
1365 {
1366   if (!fDbgOptionsP) return; // security
1367   bool withTime = fDbgOptionsP->fTimestampStructure;
1368   string comment;
1369   #if SYDEBUG>1
1370   if (!fBlockHistory && aBlockName) {
1371     // no blocks open any more and not close-all-remaining call (log close...)
1372     DebugPrintf(TDBG_LOCATION_ARG DBG_EXOTIC+DBG_ERROR,"Block Nest Warning: Trying to close block '%s', but no block is open",aBlockName);
1373   }
1374   #endif
1375   while (fBlockHistory) {
1376     // prepare comment
1377     comment.erase();
1378     if (aCloseComment) {
1379       comment += " - ";
1380       comment += aCloseComment;
1381     }
1382     // check if closing top-of-stack Block now
1383     bool found=
1384       (aBlockName && strucmp(aBlockName,fBlockHistory->fBlockName.c_str())==0);
1385     if (!found && fBlockHistory->fNext==NULL) {
1386       // last Block always counts as "found"...
1387       found = true;
1388       #if SYDEBUG>1
1389       // ...but issue warning as name is not what we would have expected
1390       StringObjAppendPrintf(comment, " - Block Nest Warning: closing '%s', but expected '%s'",aBlockName ? aBlockName : "<unknown>", fBlockHistory->fBlockName.c_str());
1391       #endif
1392     }
1393     // now close topmost Block
1394     string ts,bl;
1395     // - get time if needed and possibly put it within indented block
1396     if (withTime) {
1397       StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1398       // for XML, the time must be shown before the close tag on a separate line
1399       if (fDbgOptionsP->fOutputFormat == dbgfmt_xml) {
1400         StringObjPrintf(bl,"<endblock time=\"%s\"/>",ts.c_str());
1401         DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size()); // still within block, indented
1402       }
1403     }
1404     // - now unindent
1405     if (fIndent>0) fIndent--;
1406     // - then create closing Block
1407     #if SYDEBUG>1
1408     if (!found) StringObjAppendPrintf(comment," - Block Nest Warning: implicitly closed (by explicitly closing '%s')",aBlockName ? aBlockName : "<unknown parent>");
1409     #endif
1410     switch (fDbgOptionsP->fOutputFormat) {
1411       // XML
1412       case dbgfmt_xml:
1413         bl="</";
1414         bl+=fBlockHistory->fBlockName;
1415         bl+=">";
1416         if (!comment.empty()) {
1417           bl+=" <!-- ";
1418           bl+=comment;
1419           bl+=" -->";
1420         }
1421         break;
1422       // HTML
1423       case dbgfmt_html:
1424         bl="</ul><span class=\"block\">"; // end of content list
1425         if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1426           StringObjAppendPrintf(bl,
1427             "<span class=\"coll\" onclick=\"coll('%ld')\">&ndash;</span>",
1428             long(fBlockHistory->fBlockNo)
1429           );
1430         }
1431         StringObjAppendPrintf(bl,"<a name=\"F%ld\">",long(fBlockHistory->fBlockNo));
1432         if (withTime) {
1433           bl += MAKEDBGLINK(string("[") + ts + "] ");
1434         }
1435         bl += "End of '";
1436         bl+=fBlockHistory->fBlockName;
1437         bl+="'";
1438         bl+=comment;
1439         bl+="</a></span>";
1440         // link to top of block
1441         StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#H%ld\">[->top]</a>",long(fBlockHistory->fBlockNo));
1442         // link to end of enclosing block (if any)
1443         if (fBlockHistory->fNext) {
1444           StringObjAppendPrintf(bl,"&nbsp;<a class=\"jump\" href=\"#F%ld\">[->enclosing]</a>",long(fBlockHistory->fNext->fBlockNo));
1445         }
1446         if (fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1447           bl+="</div>"; // end of folding division
1448         }
1449         bl+="</li>"; // end of list entry containing entire block
1450         break;
1451       // plain text
1452       default:
1453       case dbgfmt_text:
1454         bl.erase();
1455         if (!fDbgOptionsP->fTimestampForAll && withTime) { // avoid timestamp here if all lines get timestamped anyway
1456           bl+="[" + ts + "] ";
1457         }
1458         bl+="End of '";
1459         bl+=fBlockHistory->fBlockName;
1460         bl+="'";
1461         bl+=comment;
1462         break;
1463     } // switch Block close
1464     // - output closing Block line
1465     DebugPutLine(TDBG_LOCATION_NONE bl.c_str(), bl.size());
1466     // - remove Block level
1467     TBlockLevel *closedLevel = fBlockHistory;
1468     fBlockHistory = closedLevel->fNext;
1469     delete closedLevel;
1470     // if we have found the Block, exit here
1471     if (found) break;
1472   }
1473 } // TDebugLoggerBase::internalCloseBlocks
1474
1475 #ifdef USE_DLT
1476 static void RegisterContext(DltContext *aHandle, const char *aContextID, const char *aDescription)
1477 {
1478   std::string envName = "LIBSYNTHESIS_";
1479   envName += aContextID;
1480   const char *value = getenv(envName.c_str());
1481   if (value) {
1482     // Explicit level.
1483     DltLogLevelType level = (DltLogLevelType)atoi(value);
1484     dlt_register_context_ll_ts(aHandle, aContextID, aDescription, level, DLT_TRACE_STATUS_OFF);
1485   } else {
1486     // Default level.
1487     dlt_register_context(aHandle, aContextID, aDescription);
1488   }
1489 }
1490 #endif // USE_DLT
1491
1492 // start debugging output if needed and sets fOutStarted
1493 bool TDebugLoggerBase::DebugStartOutput(void)
1494 {
1495   if (!fOutStarted) {
1496 #ifdef USE_DLT
1497     if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_dlt) {
1498       // Register our logging contexts.
1499       if (!DbgDLTInitialized) {
1500         RegisterContext(&DbgProtoContext, "PROT", "SyncML protocol related information");
1501         RegisterContext(&DbgSessionContext, "SESS", "session management related information");
1502         RegisterContext(&DbgAdminContext, "ADMN", "verything that has to do with administrative data (anchors, targets, map table)");
1503         RegisterContext(&DbgDataContext, "DATA", "Everything that has to do with handling user data (data objects). Actual user data will however be shown only if loglevel >= debug.");
1504         RegisterContext(&DbgRemoteInfoContext, "REMI", "This shows information delivered in the remote party's device information, such as manufacturer name, datatypes supported, fields supported etc.");
1505         RegisterContext(&DbgParseContext, "PARS", "This shows information related to parsing and processing incoming data from the remote party. Actual user data will however be shown only if loglevel >= debug.");
1506         RegisterContext(&DbgGenerateContext, "GEN", "This shows information related to generating outgoing data for the remote party. Actual user data will however be shown only if loglevel >= debug.");
1507         RegisterContext(&DbgTranspContext, "TRNS", "shows transport (http and TCP communication) related information");
1508         RegisterContext(&DbgSyncMLTKContext, "SMLT", "messages generated by the SyncML Toolkit code");
1509         RegisterContext(&DbgDefaultContext, "SYS", "any other libsynthesis debug log message that does not fit in any of the other contexts");
1510         DbgDLTInitialized = true;
1511       }
1512       fOutStarted = true;
1513     }
1514     else
1515 #endif // USE_DLT
1516     if (fOutputLoggerP) {
1517       // using another logger, call it to start output
1518       fOutStarted = fOutputLoggerP->DebugStartOutput();
1519       if (fOutStarted) {
1520         // start with indent level of parent logger
1521         fIndent = fOutputLoggerP->fIndent;
1522         // note: we'll use the parent logger's block number...
1523         fBlockNo = 0; // ...but init to something just in case
1524       }
1525     }
1526     else if (fDbgOptionsP && fDbgOutP && !fDbgPath.empty()) {
1527       // try to open the debug channel (force to openclose if we have multiple threads mixed in one file)
1528       if (fDbgOutP->openDbg(
1529         fDbgPath.c_str(),
1530         DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat],
1531         fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix ? dbgflush_openclose : fDbgOptionsP->fFlushMode,
1532         !fDbgOptionsP->fAppend
1533       )) {
1534         // make sure we don't recurse when we produce some output
1535         fOutStarted = true;
1536         fIndent = 0; // reset to make sure
1537         // create a block number that is unique in the file, even if we append multiple times.
1538         // We assume that a block consumes at least 256 bytes, so size_of_file/256 always gets
1539         // an unused block ID within that file
1540         // 256 is a safe assumption because the "fold" button <divs> alone are around 250 bytes
1541         fBlockNo = 1 + (fDbgOutP->dbgFileSize()/256);
1542         // now create required prefix
1543         DebugPutLine(TDBG_LOCATION_NONE fDbgOptionsP->fCustomPrefix.empty() ? DbgOutDefaultPrefixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomPrefix.c_str());
1544         // add folding javascript if needed
1545         if (fDbgOptionsP->fOutputFormat==dbgfmt_html && fDbgOptionsP->fFoldingMode!=dbgfold_none) {
1546           DebugPutLine(TDBG_LOCATION_NONE FoldingPrefix);
1547         }
1548       } // debug channel opened successfully
1549     } // use own debug channel
1550   } // environment ready to start output
1551   return fOutStarted;
1552 } // TDebugLoggerBase::DebugStartOutput
1553
1554
1555 // @brief finalize debugging output (close Blocks, close output channel)
1556 void TDebugLoggerBase::DebugFinalizeOutput(void)
1557 {
1558   if (fOutputLoggerP) {
1559     // just close my own blocks
1560     internalCloseBlocks(TDBG_LOCATION_NONE NULL,"closed because sub-log ends here");
1561   }
1562   if (fOutStarted && fDbgOptionsP && fDbgOutP) {
1563     // close all left-open open Blocks
1564     internalCloseBlocks(TDBG_LOCATION_NONE NULL,"closed because log ends here");
1565     // now finalize output
1566     // - special stuff before
1567     if (fDbgOptionsP->fOutputFormat == dbgfmt_xml)
1568       fIndent=0; // unindent to zero (document is not a real Block)
1569     // - then suffix
1570     DebugPutLine(TDBG_LOCATION_NONE fDbgOptionsP->fCustomSuffix.empty() ? DbgOutDefaultSuffixes[fDbgOptionsP->fOutputFormat] : fDbgOptionsP->fCustomSuffix.c_str());
1571     // now close the debug channel
1572     fDbgOutP->closeDbg();
1573   }
1574   // whatever happened, we are not started any more
1575   fOutStarted=false;
1576 } // TDebugLoggerBase::DebugFinalizeOutput
1577
1578
1579 // Output single line to debug channel (includes indenting and other prefixing, but no further formatting)
1580 void TDebugLoggerBase::DebugPutLine(TDBG_LOCATION_PROTO cAppCharP aText, stringSize aTextSize, bool aPre)
1581 {
1582 #ifdef USE_DLT
1583   if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_dlt) {
1584     // One example where this gets called is DebugVOpen/CloseBlock()
1585     // with lines prepared as if we are printing plain text. Use
1586     // a fairly neutral log level here.
1587     if (aText) {
1588       if (aTextSize > 0 && aTextSize < strlen(aText)) {
1589         string buffer(aText, aTextSize);
1590         DLT_LOG(DbgDefaultContext, DLT_LOG_INFO, DLT_STRING(buffer.c_str()));
1591       } else {
1592         DLT_LOG(DbgDefaultContext, DLT_LOG_INFO, DLT_STRING(aText));
1593       }
1594     }
1595     return;
1596   }
1597 #endif // USE_DLT
1598
1599   if (!aText || (!fDbgOutP && !fOutputLoggerP)) return;
1600   if (*aText) {
1601     // not an empty line
1602     string msg;
1603     msg.erase();
1604     // prefix with timestamp if selected in text format
1605     if (fDbgOptionsP && fDbgOptionsP->fOutputFormat==dbgfmt_text && fDbgOptionsP->fTimestampForAll) {
1606       // prefix each line (before the indent!) with a timestamp
1607       string ts;
1608       StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1609       msg='[';
1610       msg+=ts;
1611       msg+="] ";
1612     }
1613     // Indent if selected
1614     if (fDbgOptionsP && !fDbgOptionsP->fIndentString.empty() && !(fDbgOptionsP->fOutputFormat==dbgfmt_html && aPre)) {
1615       // with indent
1616       for (uInt16 n=0; n<fIndent; n++) {
1617         msg+=fDbgOptionsP->fIndentString;
1618       }
1619     }
1620     // add message itself
1621     if (aTextSize)
1622       msg.append(aText,aTextSize);
1623     else
1624       msg.append(aText);
1625     // now output
1626     if (fOutputLoggerP) {
1627       // use parent's output
1628       fOutputLoggerP->fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now
1629     }
1630     else {
1631       // use my own output channel
1632       fDbgOutP->putLine(msg.c_str(),false); // %%% no forceflush for now
1633     }
1634   }
1635 } // TDebugLoggerBase::DebugPutLine
1636
1637
1638 // TDebugLogger implementation
1639 // ---------------------------
1640
1641 // constructor
1642 TDebugLogger::TDebugLogger(GZones *aGZonesP) :
1643   inherited(aGZonesP)
1644 {
1645   #ifdef MULTI_THREAD_SUPPORT
1646   fMainThreadID=0;
1647   fSubThreadLogs=NULL;
1648   fSilentLoggerP=NULL;
1649   #endif
1650 } // TDebugLogger::TDebugLogger
1651
1652
1653 // destructor
1654 TDebugLogger::~TDebugLogger()
1655 {
1656   #ifdef MULTI_THREAD_SUPPORT
1657   // remove subthread loggers
1658   TSubThreadLog* subThreadP = fSubThreadLogs;
1659   fSubThreadLogs = NULL;
1660   while (subThreadP) {
1661     // delete logger if any
1662     if (subThreadP->fSubThreadLogger) {
1663       SYSYNC_TRY {
1664         delete subThreadP->fSubThreadLogger;
1665       }
1666       SYSYNC_CATCH(...)
1667         // nop
1668       SYSYNC_ENDCATCH
1669     }
1670     TSubThreadLog* delP = subThreadP;
1671     subThreadP = subThreadP->fNext;
1672     delete delP;
1673   }
1674   //
1675   if (fSilentLoggerP) {
1676     delete fSilentLoggerP;
1677     fSilentLoggerP = NULL;
1678   }
1679   #endif
1680 } // TDebugLogger::~TDebugLogger
1681
1682
1683 #ifdef MULTI_THREAD_SUPPORT
1684
1685 void TDebugLogger::setOptions(const TDbgOptions *aDbgOptionsP)
1686 {
1687   TDebugLoggerBase::setOptions(aDbgOptionsP);
1688   TSubThreadLog* subThreadP = fSubThreadLogs;
1689   while (subThreadP) {
1690     if (subThreadP->fSubThreadLogger) {
1691       subThreadP->fSubThreadLogger->setOptions(aDbgOptionsP);
1692     }
1693     subThreadP = subThreadP->fNext;
1694   }
1695 }
1696
1697 /// @brief find (and possibly delete) subthread record
1698 /// @param aAndRemove[in] if set, the subthread record will be removed in a thread safe way
1699 ///        IF AND ONLY IF aThreadID is the calling thread (i.e. only own thread may be removed from the list)!
1700 ///        Note that the caller must take care of deleting the subthread record
1701 TSubThreadLog *TDebugLogger::findSubThread(uInt32 aThreadID, bool aAndRemove)
1702 {
1703   TSubThreadLog* subThreadP = fSubThreadLogs;
1704   TSubThreadLog** subThreadLinkPP = &fSubThreadLogs;
1705   while (subThreadP) {
1706     if (subThreadP->fThreadID == aThreadID) {
1707       if (aAndRemove) {
1708         // bridge previous with next in one single assignment (i.e. thread safe)
1709         *subThreadLinkPP = subThreadP->fNext;
1710       }
1711       // return found record (note that it MUST BE DELETED by caller if no longer used)
1712       return subThreadP;
1713     }
1714     subThreadLinkPP = &subThreadP->fNext;
1715     subThreadP = *subThreadLinkPP;
1716   }
1717   return NULL; // none found
1718 } // TDebugLogger::findSubThread
1719
1720
1721 /// @brief find or create logger for subthread
1722 TDebugLoggerBase *TDebugLogger::getThreadLogger(bool aCreateNew)
1723 {
1724   if (fOutputLoggerP) {
1725     TDebugLoggerBase *logger = fOutputLoggerP->getThreadLogger(aCreateNew);
1726     if (logger)
1727       return logger;
1728   }
1729
1730   if (!fDbgOptionsP || fDbgOptionsP->fSubThreadMode==dbgsubthread_none)
1731     return this; // no options, do not handle subthreads specially
1732   uIntArch threadID = myThreadID();
1733   if (fDbgOptionsP->fSubThreadMode==dbgsubthread_linemix || threadID==fMainThreadID) {
1734     // In line mix and for mainthread - I am the logger for this thread!
1735     return this;
1736   }
1737   TSubThreadLog* subThreadP = findSubThread(threadID);
1738   if (subThreadP) {
1739     // we know this subthread, return its logger
1740     return subThreadP->fSubThreadLogger; // can be NULL if subthread logging is disabled
1741   }
1742   // unknown subthread
1743   if (fMainThreadID==0) {
1744     // no current mainthread, let subthread write to main log
1745     // Note: this makes sure log info possibly trailing the DebugThreadOutputDone()
1746     //       also lands in the main log. This is not critical - the only thing that must be
1747     //       ensured is that starting new threads is made only with DebugDefineMainThread set.
1748     return this;
1749   }
1750   // new subthread, create entry in list
1751   if (aCreateNew) {
1752     string s;
1753     // create new entry
1754     subThreadP = new TSubThreadLog;
1755     subThreadP->fThreadID=threadID;
1756     subThreadP->fNext=fSubThreadLogs; // link current list behind this new entry
1757     // create logger for the thread (or none)
1758     switch (fDbgOptionsP->fSubThreadMode) {
1759       case dbgsubthread_separate:
1760         // separate file for subthread output
1761         // - create new base logger
1762         subThreadP->fSubThreadLogger = new TDebugLoggerBase(fGZonesP);
1763         // - install output (copy)
1764         subThreadP->fSubThreadLogger->installOutput(fDbgOutP ? fDbgOutP->clone() : NULL);
1765         // - same options
1766         subThreadP->fSubThreadLogger->setOptions(getOptions());
1767         // - inherit current mask/enable
1768         subThreadP->fSubThreadLogger->setMask(getMask());
1769         subThreadP->fSubThreadLogger->setEnabled(fDebugEnabled);
1770         // - debug path is same as myself plus Thread ID
1771         subThreadP->fSubThreadLogger->setDebugPath(fDbgPath.c_str());
1772         StringObjPrintf(s,"_%lu",(long unsigned)threadID);
1773         subThreadP->fSubThreadLogger->appendToDebugPath(s.c_str());
1774         break;
1775       case dbgsubthread_suppress:
1776       default:
1777         // no output from subthreads
1778         subThreadP->fSubThreadLogger=NULL; // no logger
1779         break;
1780     }
1781     // now activate by linking it at top of list (this is thread safe)
1782     fSubThreadLogs = subThreadP;
1783     // return the logger
1784     return subThreadP->fSubThreadLogger;
1785   }
1786   return NULL; // no logger for this thread
1787 } // TDebugLogger::getThreadLogger
1788
1789
1790 // helper needed for maintaining old DEBUGPRINTFX() macro syntax
1791 TDebugLoggerBase &TDebugLogger::setNextMask(uInt32 aDbgMask)
1792 {
1793   TDebugLoggerBase *loggerP = getThreadLogger();
1794   if (loggerP) {
1795     // return pointer to loggerbase whose DebugPrintfLastMask() must be called
1796     return loggerP->inherited::setNextMask(aDbgMask);
1797   }
1798   else {
1799     // we have no logger but still need to return something
1800     if (!fSilentLoggerP) {
1801       fSilentLoggerP = new TDebugLoggerBase(fGZonesP);
1802       fSilentLoggerP->setEnabled(false);
1803     }
1804     fSilentLoggerP->setNextMask(DBG_ERROR); // must set non-zero to make sure it is NOT output!
1805     return *fSilentLoggerP;
1806   }
1807 } // TDebugLoggerBase::setNextMask
1808
1809
1810 // output text to debug channel, with checking for subthreads
1811 void TDebugLogger::DebugPuts(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
1812 {
1813   TDebugLoggerBase *loggerP = getThreadLogger();
1814   if (loggerP) loggerP->inherited::DebugPuts(TDBG_LOCATION_ARG aDbgMask,aText,aTextSize,aPreFormatted);
1815 } // TDebugLogger::DebugPuts
1816
1817
1818 void TDebugLogger::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs)
1819 {
1820   TDebugLoggerBase *loggerP = getThreadLogger();
1821   if (loggerP) loggerP->inherited::DebugVPrintf(TDBG_LOCATION_ARG aDbgMask,aFormat,aArgs);
1822 } // TDebugLogger::DebugVPrintf
1823
1824
1825 void TDebugLogger::DebugVOpenBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs)
1826 {
1827   TDebugLoggerBase *loggerP = getThreadLogger();
1828   if (loggerP) loggerP->inherited::DebugVOpenBlock(TDBG_LOCATION_ARG aBlockName, aBlockTitle, aCollapsed, aBlockFmt, aArgs);
1829 } // TDebugLogger::DebugVOpenBlock
1830
1831
1832 void TDebugLogger:: DebugCloseBlock(TDBG_LOCATION_PROTO cAppCharP aBlockName)
1833 {
1834   TDebugLoggerBase *loggerP = getThreadLogger();
1835   if (loggerP) loggerP->inherited::DebugCloseBlock(TDBG_LOCATION_ARG aBlockName);
1836 } // TDebugLogger::DebugCloseBlock
1837
1838 #endif
1839
1840
1841 // output all buffered subthread's output in a special subthread Block in the main output
1842 void TDebugLogger::DebugShowSubThreadOutput(void)
1843 {
1844   #ifdef MULTI_THREAD_SUPPORT
1845   // nop as long mixed-block mode is not implemented
1846   #endif
1847 } // TDebugLogger::DebugShowSubThreadOutput
1848
1849
1850 // the calling thread signals that it is done with doing output for now. If the main
1851 // thread is doing this and we have bufferandmix mode, the next subthread will be allowed
1852 // to write into the output channel until a new main thread gains control via
1853 // DebugDefineMainThread();
1854 void TDebugLogger::DebugThreadOutputDone(bool aRemoveIt)
1855 {
1856   #ifdef MULTI_THREAD_SUPPORT
1857   uIntArch threadID = myThreadID();
1858   if (threadID==fMainThreadID) {
1859     // current main thread done
1860     fMainThreadID = 0;
1861   }
1862   // for session logs, subthreads are usually left in the list at this time (aRemoveIt==false)
1863   // (as they will get deleted with the session logger later anyway)
1864   if (aRemoveIt) {
1865     TSubThreadLog* tP = findSubThread(threadID,true);
1866     if (tP) {
1867       if (tP->fSubThreadLogger) {
1868         SYSYNC_TRY {
1869           delete tP->fSubThreadLogger;
1870         }
1871         SYSYNC_CATCH(...)
1872           // nop
1873         SYSYNC_ENDCATCH
1874       }
1875       delete tP;
1876     }
1877   }
1878   #endif
1879 } // TDebugLogger::DebugThreadOutputDone
1880
1881
1882 // Used to regain control as main thread (e.g. for the next request of a session which
1883 // possibly occurs from another thread).
1884 void TDebugLogger::DebugDefineMainThread(void)
1885 {
1886   #ifdef MULTI_THREAD_SUPPORT
1887   uIntArch threadID = myThreadID();
1888   // if this is already the main thread, no op
1889   if (threadID == fMainThreadID)
1890     return; // nop, done
1891   // thread is not the current main thread
1892   // - search if it is a registered subthread
1893   TSubThreadLog *subThreadP = findSubThread(threadID);
1894   if (fMainThreadID==0) {
1895     // no main thread currently registered
1896     if (subThreadP!=NULL) {
1897       // this is not a new thread, but a known subthread, can't get main thread now
1898       return; // no further op
1899     }
1900     else {
1901       // this is not a known subthread, so it can become the main thread
1902       fMainThreadID = threadID;
1903       return; // done
1904     }
1905   }
1906   else {
1907     // cannot become main thread, will be treated as subthread if it generates output
1908     // - no op required
1909   }
1910   #endif
1911 } // TDebugLogger::DebugDefineMainThread
1912
1913
1914 } // namespace sysync
1915
1916 #endif // SYDEBUG
1917
1918 // eof
1919