DebStream.cc 24.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
// (C) Copyright 2014 by Autodesk, Inc.
//
// The information contained herein is confidential, proprietary
// to Autodesk,  Inc.,  and considered a trade secret as defined
// in section 499C of the penal code of the State of California.
// Use of  this information  by  anyone  other  than  authorized
// employees of Autodesk, Inc.  is granted  only under a written
// non-disclosure agreement,  expressly  prescribing  the  scope
// and manner of such use.

11
#include "DebUtils.hh"
12
#include "Base/Utils/ThrowError.hh"
13
14
15

#ifdef DEB_ON

16
17
18
19
20
#include <string>
#include <fstream>
#include <time.h>
#include <vector>
#include <iostream>
21
#include <map>
22
23
24
25
#include <memory>
#include <list>
#include <map>
#include <sstream>
26
27
28
29
30
31
#include <cstring>
#include <time.h>

#ifndef WIN32
  #define sprintf_s snprintf
#endif
32

33
34
namespace { // LOCAL_PROC
bool is_html_filename(const char* const str)
35
{
36
  if (str == nullptr) return false;
37
  const char* dot = strrchr(str, '.');
38
39
  if (dot == nullptr) return false;
  ++dot;
40
  return (!strncmp(dot, "htm", 3)) || (!strncmp(dot, "HTM", 3)) ;
41
}
42
}
43

44
namespace Debug {
45
46

class FunctionCallSequence
47
{
48
  std::string func_name_;
49
  std::vector<Enter*> debs_;  // These may not have sequential counts when multithreaded.
50
51

public:
52
  FunctionCallSequence(const char* _func_name, Enter* _deb)
53
    : func_name_(_func_name)
54
  {
55
56
57
    debs_.push_back(_deb);
  }
  ~FunctionCallSequence() {}
58

59
  bool add(const char* _func_name, Enter* _deb)
60
61
  {
    if (func_name_ == _func_name)
62
    {
63
      debs_.push_back(_deb);
64
      return true;
65
    }
66
67
    return false;
  }
68

69
70
71
  bool pop()
  {
    if (debs_.size() > 1)
72
    {
73
74
      debs_.pop_back();
      return true;
75
    }
76
77
78
    debs_.clear();
    return false;
  }
79

80
81
82
83
84
  int number_calls() const
  {
    if (!this) return 0;
    return (int)debs_.size();
  }
85

86
87
88
  int count(int i = 0) const
  {
    int num = number_calls();
89
    if (i < num) return debs_[num - 1 - i]->nmbr_;
90
91
    return -1;
  }
92

93
94
95
96
  const char* name() const
  {
    return func_name_.c_str();
  }
97

98
99
100
101
102
103
  // Replace interior of < > in function name with . for brevity
  void compact_name(std::string& str) const
  {
    int cnt = 0;
    const char* ptr = func_name_.c_str();
    while (ptr && (*ptr != '\0'))
104
    {
105
106
107
108
      char c = *ptr;
      if (c == '>') --cnt;
      if (cnt == 0) str.append(1, c);
      if (c == '<')
109
      {
110
111
        if (cnt == 0) str.append(".");
        ++cnt;
112
      }
113
      ++ptr;
114
    }
115
  }
116

117
118
119
120
121
122
123
  // Get single call stack element string
  void get(std::string& _str, const bool _strip_angled, bool _with_counts) const
  {
    if (_strip_angled) compact_name(_str);
    else _str.append(name());
    _str.append("[");
    if (_with_counts)
124
    {
125
126
127
128
129
      int num = number_calls();
      int prev = -2;
      int seq_cnt = 0;
      for (int i = 0; i < num; ++i)
      {
130
        int cnt = debs_[i]->nmbr_;
131
        if (cnt != prev + 1)
132
        {
133
          char buffer[64];
134

135
136
137
138
          if (seq_cnt > 0)
          {
            _str.append("-");
            sprintf_s(buffer, sizeof(buffer), "%i", prev);
139
            _str.append(buffer);
140
          }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
          if (i > 0) _str.append(",");
          sprintf_s(buffer, sizeof(buffer), "%i", cnt);
          _str.append(buffer);
          seq_cnt = 0;
        }
        else
          ++seq_cnt;
        prev = cnt;
      } // endfor i
    } // endif _with_counts
    else
      _str.append("*");
    _str.append("]");
  } // endfunc get
155

156
  void get_indent(std::string& _str, File* _dfile, bool _is_html);
157

158
}; // endclass FunctionCallSequence
159
160


161
162
163
164
165
166
167
class CallStack
{
  std::vector<FunctionCallSequence> calls_;
  int depth_;
public:
  CallStack() : depth_(0) {}
  ~CallStack() {}
168

169
  void add(const char* _func_name, Enter* _deb)
170
171
172
173
174
  {
    if (calls_.empty() || !calls_.back().add(_func_name, _deb))
      calls_.push_back(FunctionCallSequence(_func_name, _deb));
    ++depth_;
  }
175

176
177
178
179
180
181
  void pop()
  {
    if (!calls_.back().pop())
      calls_.pop_back();
    --depth_;
  }
182

183
  const FunctionCallSequence* call(int _up = 0) const
184
185
186
187
188
  {
    int num = (int)calls_.size();
    if (_up < num) return &calls_[num - 1 - _up];
    return nullptr;
  }
189

190
191
192
193
194
  // Read a particular call stack element
  bool read(int _up, const char*& _funcname, int& _count)
  {
    const FunctionCallSequence* fcs = call(_up);
    if (fcs != nullptr)
195
    {
196
197
198
      _funcname = fcs->name();
      _count = fcs->count(0); // Return most recent deb_enter_count
      return true;
199
    }
200
201
    return false;
  }
202

203
204
205
206
207
208
  // Get a full call stack sting.
  // returns number of call stack function elements
  int get(std::string& _str, bool _with_counts = true) const
  {
    int num = (int)calls_.size();
    for (int i = 0; i < num; ++i)
209
    {
210
211
      if (i > 0) _str.append("->");
      calls_[i].get(_str, true, _with_counts);
212
    }
213
214
    return num;
  }
215

216
217
218
219
  int depth() const
  {
    return depth_;
  }
220

221
  bool get_indent(std::string& _str, File* _dfile, const bool is_html);
222
}; // endclass CallStack
223

224
class File
225
226
{
private:
227
228
229
230
231
  class module_stats
  {
  public:
    int lev_; // Level set for this module
    int col_; // Colour (24 bit RGB, MSB=Red) for this module's DEB_out
232
    module_stats(int _lev = 0) : lev_(_lev), col_(0x808080) {}
233
234
    ~module_stats() {}
  };
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  
  // We use this data to decide the debug level of a function in a file.
  class FilterLevelSelector
  {
  public:
    void add_file_string(const std::string& _str) { file_selct_strngs_.push_back(_str); }
    void add_func_string(const std::string& _str) { func_selct_strngs_.push_back(_str); }

    bool select_file(const char* _flnm) const
    {
      std::string flnm(_flnm);
      const std::string root_dir("ReForm");
      auto pos = flnm.rfind(root_dir);
      if (pos != std::string::npos)
        flnm = flnm.substr(pos + root_dir.size());
      return search(flnm, file_selct_strngs_);
    }

    bool select_function(const char* _func) const
    {
      return search(_func, func_selct_strngs_);
    }
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  private:
    static bool search(const std::string& _name,
      const std::list<std::string>& _sel_strings)
    {
      for (const auto& sel : _sel_strings)
      {
        if (_name.find(sel) != std::string::npos)
          return true;
      }
      return false;
    }

  private:
    std::list<std::string> file_selct_strngs_; // list of strings to be found inside the file name.
    std::list<std::string> func_selct_strngs_; // list of strings to be found inside the function name.
  };
274

275
  Stream::StreamType type_;
276
277
  int lev_;
  int num_flush_;
278
  int priority_; // Last permission granted
279
  int indent_size_;
280

281
282
  std::map<int, FilterLevelSelector> level_selc_map_; // A map filter_level ==> filter_selector

283
284
  bool at_line_start_;

285
286
287
288
  std::string current_;
  std::string output_;
  std::string file_name_;
  std::fstream file_stream_;
289
290

  std::string double_format_;
291
  //std::string indent_string_;
292
  Stream* deb_stream_;
293
294
295
296
  CallStack call_stack_;

public:

297
298
299
300
  CallStack& call_stack()
  {
    return call_stack_;
  }
301

302
303
  bool is_kept_open() const
  {
304
    return 0 != (type_ & Stream::StreamType::KEEP_OPEN);
305
306
307
  }
  bool is_html() const
  {
308
    return 0 != (type_ & Stream::StreamType::HTML);
309
310
311
  }
  bool is_retained() const
  {
312
    return 0 != (type_ & Stream::StreamType::RETAIN);
313
314
315
  }
  bool is_appended() const
  {
316
    return 0 != (type_ & Stream::StreamType::APPEND);
317
  }
318
  // Only applies to HTML DEB_out
319
320
321
322
323
324
325
326
  bool is_white_on_black() const
  {
    return true;
  }
  int indent_size()
  {
    return indent_size_;
  }
327

328
329
330
331
332
333
334
335
  bool file_is_open() const
  {
    return file_stream_.is_open();
  }
  int priority() const
  {
    return priority_;
  }
336

337
338
339
340
341
342
343
344
  bool fork_to_cout()
  {
    return false;
  }
  bool fork_to_cerr()
  {
    return true;
  }
345
346


347
  const char* file_name() const
348
  {
349
    if (this && (!file_name_.empty())) return file_name_.c_str();
350
351
    return nullptr;
  }
352

353
354
355
356
357
358
  void clear()
  {
    current_.clear();
    output_.clear();
    file_name_.clear();
  }
359

360
361
362
363
364
365
366
  char prev_char() const
  {
    if (!current_.empty()) return current_.back();
    if (!output_.empty()) return output_.back();
    return '\0';
  }

367

368
  void indent(bool _full_text)
369
  {
370
    std::string str;
371
    if (call_stack().get_indent(str, this,  _full_text && is_html()))
372
      current_.append(str);
373
374
  }

375
  void line_break(bool _with_indent = false)
376
  {
377
    if (is_html()) current_.append("<br>");   // Don't bother with matching </br>
378
379
    current_.append("\n", 1);

380
381
    if (_with_indent) indent(false);
    else at_line_start_ = true;
382
383
  }

384
385
386
387
388
389
  void set_level(int _lev)
  {
    lev_ = _lev;
  }

  void print_direct(const std::string& _s)
390
391
392
393
  {
    current_.append(_s);
  }

394
395
  void print(const char _c)
  {
396
    if (_c == '\n')
397
398
399
400
401
    {
      line_break();
      return;
    }

402
403
    if (at_line_start_)
    {
404
      indent(true);
405
406
407
408
      at_line_start_ = false;
    }


409
410
411
    if (is_html())
    {
      // translate the esoteric characters used in IGM DEB_out
412

413
      if (_c == '')  // -62
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
        return;

      if (_c == '') // 167 = -89
      {
        current_.append("&sect;");
        return;
      }

      if (_c == '') // -80
      {
        current_.append("&deg;");
        return;
      }
    }
    current_.append(&_c, 1);
429
430
  }

431
432
433
434
435
  void print_to_ostream(const char* const _s, std::ostream& os)
  {
    os << _s;
  }

436
  void print(const char* const _s, bool _fork = true)
437
438
439
  {
    if (_s != nullptr)
    {
440
      for (int i = 0; ; ++i)
441
442
      {
        const char c = _s[i];
443
        if (c == '\0') break;
444
445
446
447
        print(c);
      }
      if (_fork)
      {
448
        if (fork_to_cout())
449
          print_to_ostream(_s, std::cout);
450
        if (fork_to_cerr())
451
          print_to_ostream(_s, std::cerr);
452
      }
453
    }
454
455
456
457
458
459
460
461
  }

  void print(int _i)
  {
    char buffer[64];
    sprintf_s(buffer, sizeof(buffer), "%i", _i);
    print(buffer);
  }
462

463

464
  const char* double_format() const
465
466
467
468
469
470
  {
    if (double_format_.empty())
      return "%.17g";
    return double_format_.c_str();
  }

471
  void set_double_format(const char* const str)
472
  {
473
    if (str == nullptr)
474
475
476
477
478
479
      double_format_.clear();
    else
      double_format_ = str;
  }


480
481
482
  void print(double _d)
  {
    char buffer[64];
483
    sprintf_s(buffer, sizeof(buffer), double_format(), _d);
484
485
486
    print(buffer);
  }

487
  void print(const Command& _co)
488
  {
489
490
    switch (_co.com())
    {
491
    case Command::END :
492
      if (is_html()) print_direct("</FONT>");
493
      break;
494
    case Command::END_ERR :
495
496
497
    // Powerdown DEB_error font
    // if (is_html()) print_direct("</BLINK>");
    // fall through
498
    case Command::END_LF :
499
      if (is_html()) print_direct("</FONT>");
500
501
502
503
504
505
506
507
508
      line_break(); 
      
      // line_break() does not fork to cout or cerr
      // so do so explicitly.
      if (fork_to_cout())
         std::cout << "\n";
      if (fork_to_cerr())
         std::cerr << "\n";

509
510
511
512
      break;
    }
  }

513
  Stream& stream()
514
515
516
  {
    return *deb_stream_;
  }
517

518
519
  // Append current asctime to given string
  bool add_time(std::string& str)
520
521
  {
    time_t rawtime;
522
    time(&rawtime);
523
    struct tm timeinfo;
524
525
#ifdef WIN32
    int err = localtime_s(&timeinfo, &rawtime);
526
527
528
529
530
531
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
532
        str.append(buffer);
533
534
535
        return true;
      }
    }
536
537
538
539
540
541
#else//WIN32
    //TODO: Implement a secure version of this code for Linux, OSX
    //timeinfo = *localtime(&rawtime);
    //char* buffer = asctime(&timeinfo);
    str.append("TODO: add_time()");
#endif//WIN32
542
543
544
    return false;
  }

545

546
#if 1
547
  bool hover(std::string& _str, const std::string& _hover, const bool _open)
548
549
550
551
  {
    if (is_html())
    {
      char buffer[1024];
552
553
      if (_open)  sprintf_s(buffer, sizeof(buffer),
                              "<span title=\"%s\">", _hover.c_str());
554
555
556
557
558
559
560
561
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

562
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
563
564
565
566
567
568
569
570
571
572
573
574
  {
    if (is_html())
    {
      char buffer[1024];
      if (_open)  sprintf_s(buffer, sizeof(buffer), "<A name=\"%08X_%s\">", _id, _tag);
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }

575
  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
576
577
578
579
580
581
582
583
584
585
  {
    if (is_html())
    {
      char buffer[2048];
      if (_open)
      {
        // HTML title hover text is cropped to 64 char in Firefox but displays
        // OK in Chrome. We could use javascript to avoid this limit but HTML
        // is simpler.
        if (_hover.empty()) sprintf_s(buffer, sizeof(buffer),
586
587
588
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
589
590
591
592
593
594
595
596
597
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


598
599
600
  void header(std::string& str)
  {
    if (is_html())
601
602
603
604
605
606
607
608
609
    {
      str.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\">");
      str.append("\n<HTML><HEAD>");
      str.append("\n<TITLE>ReForm DEB_out");
      str.append("</TITLE>");
      // javascript lib loads go here
      // stylesheet loads go here
      // within HEAD javascript goes here
      str.append("\n</HEAD>");
610
      if (is_white_on_black())
611
      {
612
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
613
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
614
615
616
617
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
618
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
619
620
621
622
623
624
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
625
      if (!file_name_.empty())
626
627
628
629
      {
        str.append(file_name_);
        str.append(" opened ");
      }
630
      add_time(str);
631
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
632
633
      if (is_html()) str.append("<BR>");
      str.append("\n");
634
    }
635
   }
636
637
638
639
640
641

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
642
      std::string str("\n");
643
      if (!file_name_.empty()) str.append(file_name_);
644
      str.append(" Closed: ");
645
646
      add_time(str);
      stream() << str << "\n";
647
648
649
    }

    if (is_html())
650
      stream().print("\n</BODY></HTML>", false);
651
  }
652

653
654
655
656
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
657

658
659
660
  int flush()
  {
    int res = 0;
661
    if (!current_.empty())
662
    {
663
664
      const char* fname = file_name();
      if ((fname != nullptr) || file_is_open())
665
      {
666
        if (!file_is_open())
667
        {
668
669
670
          file_stream_.open(fname,
                            std::fstream::out | ((is_appended() && !is_first_flush()) ?
                                                 std::fstream::app  :  std::fstream::trunc));
671
672
673
674
675
        }

        if (file_stream_.is_open())
        {
          std::string hdr;
676
677
678
          if (!is_appended())
          {
            // Reoutput entire file
679
680
681
682
683
684
            header(hdr);
            output_.append(hdr);
            file_stream_ << output_;
          }
          else
          {
685
            if (is_first_flush())
686
687
            {
              header(hdr);
688
689
              if (is_retained())
                output_.append(hdr);
690
691
692
693
694
695
              file_stream_ << hdr;
            }
            ++num_flush_;
          }
          file_stream_ << current_;

696
          if (is_retained())
697
698
            output_.append(current_);

699
700
          current_.clear();
          if (!is_kept_open())
701
702
703
704
705
706
            file_stream_.close();
        } // endif fs.is_open
        else
          res = -1;
      }
    } // endif current empty
707
    return res;
708
709
  }  // endfunc flush

710
711
712
713
  // Use with extreme caution.
  const char * string_out() const
 { return current_.c_str(); }

714
715
716
717
718
719
  void close()
  {
    footer();
    flush();
  }

720
  void set_file_name(const char* _name)
721
  {
722
    file_name_ = _name ? _name : "";
723
    if (is_html_filename(_name))
724
      type_ = (Stream::StreamType)(type_ | Stream::StreamType::HTML);
725
  }
726

727
  int permission(const char* const _flnm)
728
  {
729
730
    int lev = lev_;
    for (const auto& fltrs : level_selc_map_)
731
    {
732
733
734
735
736
737
      if (fltrs.second.select_file(_flnm) ||
          fltrs.second.select_function(call_stack_.call()->name()))
      {
        lev = fltrs.first;
        break;
      }
738
    }
739
740
    return lev;
  }
741
742

  bool is_at_line_start()
743
  {
744
    return at_line_start_;
745
746
  }

747
748
749
  File(
    Stream* _deb_stream,
    Stream::StreamType _type = (Stream::StreamType)(Stream::APPEND | Stream::RETAIN),
750
751
    const char* _file_name = nullptr) :
    type_(_type), lev_(5), deb_stream_(_deb_stream), num_flush_(0)
752
  {
753
    read_debug_config();
754
    set_file_name(_file_name);
755
    indent_size_ = 3;
756
    at_line_start_ = false; // Don't want to indent header
757
  }
758

759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
  void read_debug_config()
  {
    std::string flnm("reform_deb.cfg");
#ifdef WIN32
    size_t size;
    getenv_s(&size, nullptr, 0, "REFORM_DEB_CONFIG");
    if (size > 0)
    {
      std::unique_ptr<char[]> bufer(new char[size]);
      getenv_s(&size, bufer.get(), size, "REFORM_DEB_CONFIG");
      flnm = bufer.get();
    }
#else
    const char* deb_flnm = getenv("REFORM_DEB_CONFIG");
    if (deb_flnm != nullptr)
      flnm = deb_flnm;
#endif
    std::ifstream deb_stream(flnm.c_str());
    std::string line;
    while(std::getline(deb_stream, line))
    {
      std::stringstream line_stream(line);
      std::string type;
      line_stream >> type;
      void (FilterLevelSelector::*add_string)(const std::string&) = nullptr;
      if (type == "all") {}
      else if (type == "file")
        add_string = &FilterLevelSelector::add_file_string;
      else if (type == "func")
        add_string = &FilterLevelSelector::add_func_string;
      else
        continue;
      int lev;
      line_stream >> lev;
      if (lev < 0 || lev > 15)
        continue;
      if (add_string == nullptr)
      {
        lev_ = lev; // We have red the default level.
        continue;
      }
      char colon;
      line_stream >> colon;
      if (colon != ':')
        continue;
      std::string select_str;
      while(line_stream >> select_str)
        (level_selc_map_[lev].*add_string)(select_str);
    }
808
  }
809

810
}; // endclass File
811

812
813
814
815
816
817
//////////////////////////////////////////////////////////////////////////

Enter::Enter(const char* const _flnm, const char* const _fnct, 
  int& _nmbr, int& _lvl)
  : flnm_(_flnm), outs_(0), lns_(0)
{// TODO: for thread-safety we will need to make the constructor body atomic!
818
819
  stream(0, false).dfile()->call_stack().add(_fnct, this);

820
821
822
823
824
  nmbr_ = _nmbr++; 

  if (_lvl == INVALID_LEVEL)
    _lvl = Stream::get_global().dfile()->permission(flnm_);
  lvl_ = _lvl;
825

826
  static int id_cnt = 0;
827
  id_ = ++id_cnt;
828
829
}

830
Enter::~Enter()
831
{
832
  File* impl = stream(0, false).dfile();
833
  impl->call_stack().pop();
834

835
  std::string str;
836
  if (((outs_ > 0) || (lns_ > 0)) && impl->anchor(str, id_, "exit", true))
837
  {
838
    impl->anchor(str, id_, "exit", false);
839
840
    impl->print_direct(str);
  }
841
842
}

843
Stream& Enter::stream(const int _warn, const bool _print)
844
{
845
846
  Stream& ds = Stream::get_global(_warn);
  File* impl = ds.dfile();
847

848
  if (_print)
849
  {
850
851
    if (impl->is_html())
    {
852
      // bool is_deb_error = (_warn == 2);
853
      // DEB_error font powerup goes here. BLINK is deprecated sadly.
854
      // if (is_deb_error)  impl->print_direct("<BLINK>");
855
      const int col = 0xFF0000; // RED
856
      char buffer[256];
857
858
      sprintf_s(buffer, sizeof(buffer), "<FONT COLOR=\"#%06X\" SIZE=%i>",
                col, impl->priority() + 1);
859
      impl->print_direct(buffer);
860
861
    }

862
    if (outs_ < 1)
863
864
    {
      // First DEB_out in this function so output callstack, and flush.
865
      impl->indent(true);
866
867
868
869
      std::string str;
      bool is_html = impl->is_html();
      if (is_html)
      {
870
        str.append("<FONT SIZE=2><u>");
871
        impl->anchor(str, id_, "enter", true);
872
      }
873
      else
874
875
      {
        // .txt call stack lead in
876
877
878
        str.append("****>");
      }

879
      impl->call_stack().get(str);
880
      if (is_html) str.append("</u></FONT>");
881
882
      impl->print_direct(str.c_str()); // Don't fork callstack to cerr etc.
      impl->line_break();
883
      ds.dfile()->flush();
884
    }
885
    ++outs_;
886
887
888
889
890
  }

  return ds;
}

891
void FunctionCallSequence::get_indent(std::string& _str, File* _dfile, bool _is_html)
892
893
{
  int num = number_calls();
894
895
  for (int i = 0; i < num; ++i)
  {
896
    Enter* deb = debs_[i];
897
898
899
900
901
902
903
    if (_is_html)
    {
      /* HTML indent element is <L> with span title the name and count
      of the function and with < linking to the entry anchor (if present) and
      > linking to the exit anchor (will be present).
      L is the first letter of the module name */
      char hovert[1024];
904
      sprintf_s(hovert, sizeof(hovert), "%s[%i]", func_name_.c_str(), deb->nmbr_);
905
      int col = 0xFFFFFF;
906
907
908
909
910
911
912
913
      char buffer[1024];
      sprintf_s(buffer, sizeof(buffer), "<FONT COLOR=\"#%06X\">.", col);

      _dfile->hover(_str, std::string(hovert), true);

      _str.append(buffer);
      std::string cstk;
      //impl->call_stack().get(cstk);
914
      if ((deb->outs_ > 0) && _dfile->link_to(_str, deb->id_, "enter", cstk, true))
915
916
917
918
919
920
      {
        _str.append("&lt;");
        _dfile->link_to(_str, deb->id_, "enter", cstk,  false);
      }
      else _str.append("&lt;");

921
      _str.append(deb->flnm_, 1);
922
923
924
925
926

      if (_dfile->link_to(_str, deb->id_, "exit", cstk, true))
      {
        _str.append("&gt;");
        _dfile->link_to(_str, deb->id_, "exit", cstk,  false);
927
        ++deb->lns_;
928
929
930
931
932
933
934
935
936
937
      }

      _dfile->hover(_str, std::string(hovert), false);

      _str.append("</FONT>");
    } // endif html
    else _str.append("  ");
  }
}

938
bool CallStack::get_indent(std::string& _str, File* _dfile, const bool is_html)
939
940
941
942
943
944
945
946
947
948
{

  if (_dfile->indent_size() == 0) return false;
  if (is_html)
  {
    char buffer[64];
    sprintf_s(buffer, sizeof(buffer), "<FONT SIZE=%i>", _dfile->indent_size());
    _str.append(buffer);
  }
  int num = (int)calls_.size();
949
950
  int i0 = 0;
  if (!is_html) ++i0; // Don't waste whitespace on first level indent if .txt
951
  for (int i = i0; i < num; ++i)
952
    calls_[i].get_indent(_str, _dfile,  is_html);
953
954
955
956
957
  if (is_html) _str.append(":&nbsp;</FONT>\n");
  return true;
}


958
// =====================================
959
//        Stream member funcs
960
961
962
// =====================================


963
Stream::Stream(const char* _file_name, StreamType _type)
964
965
{
  // NB. disable DEB_out over this new if new contains DEB_out
966
  dfile_ = new File(this, _type, _file_name);
967
968
}

969
Stream::~Stream()
970
{
971
  if (dfile_ != nullptr)
972
  {
973
974
    dfile()->close();
    dfile()->clear();
975
    // NB. disable DEB_out over this delete if delete contains DEB_out
976
977
    delete dfile_;
    dfile_ = nullptr;
978
979
980
  }
}

981
const char * Stream::string_out() const 
982
983
{ return dfile()->string_out();}

984
Stream& Stream::print(const int _i)
985
{
986
  dfile_->print(_i);
987
988
989
  return *this;
};

990
Stream& Stream::print(const double _d)
991
{
992
  dfile_->print(_d);
993
994
995
  return *this;
};

996
Stream& Stream::print(const char* _s, bool _fork)
997
{
998
  dfile_->print(_s, _fork);
999
1000
1001
  return *this;
};

1002
Stream& Stream::print(const char _c)
1003
{
1004
1005
1006
1007
  dfile_->print(_c);
  return *this;
};

1008
Stream& Stream::print(const Command& _co)
1009
1010
{
  dfile_->print(_co);
1011
1012
1013
  return *this;
};

1014
Stream& Stream::get_global(int _warn)
1015
{
1016
  _warn;
1017
  // TODO: Replace with a Singleton?? ThreadArray??
1018
  static Stream g_ds__("reform_deb_out.txt");
1019
1020
1021
  return g_ds__;
}

1022
Stream& operator<<(Stream& _ds, const int _i)
1023
1024
1025
{
  return _ds.print(_i);
}
1026
Stream& operator<<(Stream& _ds, const double _d)
1027
1028
1029
{
  return _ds.print(_d);
}
1030
Stream& operator<<(Stream& _ds, const char* const _s)
1031
1032
1033
{
  return _ds.print(_s);
}
1034
Stream& operator<<(Stream& _ds, const char _c)
1035
1036
1037
1038
{
  return _ds.print(_c);
}

1039
Stream& operator<<(Stream& _ds, const size_t _i)