DebStream.cc 23.9 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
namespace { // LOCAL_PROC
bool is_html_filename(const char* const str)
29
{
30
  if (str == nullptr) return false;
31
  const char* dot = strrchr(str, '.');
32
33
  if (dot == nullptr) return false;
  ++dot;
34
  return (!strncmp(dot, "htm", 3)) || (!strncmp(dot, "HTM", 3)) ;
35
}
36
}
37

38
namespace Debug {
39
40

class FunctionCallSequence
41
{
42
  std::string func_name_;
43
  std::vector<Enter*> debs_;  // These may not have sequential counts when multithreaded.
44
45

public:
46
  FunctionCallSequence(const char* _func_name, Enter* _deb)
47
    : func_name_(_func_name)
48
  {
49
50
51
    debs_.push_back(_deb);
  }
  ~FunctionCallSequence() {}
52

53
  bool add(const char* _func_name, Enter* _deb)
54
55
  {
    if (func_name_ == _func_name)
56
    {
57
      debs_.push_back(_deb);
58
      return true;
59
    }
60
61
    return false;
  }
62

63
64
65
  bool pop()
  {
    if (debs_.size() > 1)
66
    {
67
68
      debs_.pop_back();
      return true;
69
    }
70
71
72
    debs_.clear();
    return false;
  }
73

74
75
76
77
78
  int number_calls() const
  {
    if (!this) return 0;
    return (int)debs_.size();
  }
79

80
81
82
  int count(int i = 0) const
  {
    int num = number_calls();
83
    if (i < num) return debs_[num - 1 - i]->nmbr_;
84
85
    return -1;
  }
86

87
88
89
90
  const char* name() const
  {
    return func_name_.c_str();
  }
91

92
93
94
95
96
97
  // 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'))
98
    {
99
100
101
102
      char c = *ptr;
      if (c == '>') --cnt;
      if (cnt == 0) str.append(1, c);
      if (c == '<')
103
      {
104
105
        if (cnt == 0) str.append(".");
        ++cnt;
106
      }
107
      ++ptr;
108
    }
109
  }
110

111
112
113
114
115
116
117
  // 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)
118
    {
119
120
121
122
123
      int num = number_calls();
      int prev = -2;
      int seq_cnt = 0;
      for (int i = 0; i < num; ++i)
      {
124
        int cnt = debs_[i]->nmbr_;
125
        if (cnt != prev + 1)
126
        {
127
          char buffer[64];
128

129
130
131
132
          if (seq_cnt > 0)
          {
            _str.append("-");
            sprintf_s(buffer, sizeof(buffer), "%i", prev);
133
            _str.append(buffer);
134
          }
135
136
137
138
139
140
141
142
143
144
145
146
147
148
          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
149

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

152
}; // endclass FunctionCallSequence
153
154


155
156
157
158
159
160
161
class CallStack
{
  std::vector<FunctionCallSequence> calls_;
  int depth_;
public:
  CallStack() : depth_(0) {}
  ~CallStack() {}
162

163
  void add(const char* _func_name, Enter* _deb)
164
165
166
167
168
  {
    if (calls_.empty() || !calls_.back().add(_func_name, _deb))
      calls_.push_back(FunctionCallSequence(_func_name, _deb));
    ++depth_;
  }
169

170
171
172
173
174
175
  void pop()
  {
    if (!calls_.back().pop())
      calls_.pop_back();
    --depth_;
  }
176

177
  const FunctionCallSequence* call(int _up = 0) const
178
179
180
181
182
  {
    int num = (int)calls_.size();
    if (_up < num) return &calls_[num - 1 - _up];
    return nullptr;
  }
183

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

197
198
199
200
201
202
  // 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)
203
    {
204
205
      if (i > 0) _str.append("->");
      calls_[i].get(_str, true, _with_counts);
206
    }
207
208
    return num;
  }
209

210
211
212
213
  int depth() const
  {
    return depth_;
  }
214

215
  bool get_indent(std::string& _str, File* _dfile, const bool is_html);
216
}; // endclass CallStack
217

218
class File
219
220
{
private:
221
222
223
224
225
  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
226
    module_stats(int _lev = 0) : lev_(_lev), col_(0x808080) {}
227
228
    ~module_stats() {}
  };
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  
  // 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_);
    }
251

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  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.
  };
268

269
  Stream::StreamType type_;
270
271
  int lev_;
  int num_flush_;
272
  int priority_; // Last permission granted
273
  int indent_size_;
274

275
276
  std::map<int, FilterLevelSelector> level_selc_map_; // A map filter_level ==> filter_selector

277
278
  bool at_line_start_;

279
280
281
282
  std::string current_;
  std::string output_;
  std::string file_name_;
  std::fstream file_stream_;
283
284

  std::string double_format_;
285
  //std::string indent_string_;
286
  Stream* deb_stream_;
287
288
289
290
  CallStack call_stack_;

public:

291
292
293
294
  CallStack& call_stack()
  {
    return call_stack_;
  }
295

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

322
323
324
325
326
327
328
329
  bool file_is_open() const
  {
    return file_stream_.is_open();
  }
  int priority() const
  {
    return priority_;
  }
330

331
332
333
334
335
336
337
338
  bool fork_to_cout()
  {
    return false;
  }
  bool fork_to_cerr()
  {
    return true;
  }
339
340


341
  const char* file_name() const
342
  {
343
    if (this && (!file_name_.empty())) return file_name_.c_str();
344
345
    return nullptr;
  }
346

347
348
349
350
351
352
  void clear()
  {
    current_.clear();
    output_.clear();
    file_name_.clear();
  }
353

354
355
356
357
358
359
360
  char prev_char() const
  {
    if (!current_.empty()) return current_.back();
    if (!output_.empty()) return output_.back();
    return '\0';
  }

361

362
  void indent(bool _full_text)
363
  {
364
    std::string str;
365
    if (call_stack().get_indent(str, this,  _full_text && is_html()))
366
      current_.append(str);
367
368
  }

369
  void line_break(bool _with_indent = false)
370
  {
371
    if (is_html()) current_.append("<br>");   // Don't bother with matching </br>
372
373
    current_.append("\n", 1);

374
375
    if (_with_indent) indent(false);
    else at_line_start_ = true;
376
377
  }

378
379
380
381
382
383
  void set_level(int _lev)
  {
    lev_ = _lev;
  }

  void print_direct(const std::string& _s)
384
385
386
387
  {
    current_.append(_s);
  }

388
389
  void print(const char _c)
  {
390
    if (_c == '\n')
391
392
393
394
395
    {
      line_break();
      return;
    }

396
397
    if (at_line_start_)
    {
398
      indent(true);
399
400
401
402
      at_line_start_ = false;
    }


403
404
405
    if (is_html())
    {
      // translate the esoteric characters used in IGM DEB_out
406

407
      if (_c == '')  // -62
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
        return;

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

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

425
426
427
428
429
  void print_to_ostream(const char* const _s, std::ostream& os)
  {
    os << _s;
  }

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

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

457

458
  const char* double_format() const
459
460
461
462
463
464
  {
    if (double_format_.empty())
      return "%.17g";
    return double_format_.c_str();
  }

465
  void set_double_format(const char* const str)
466
  {
467
    if (str == nullptr)
468
469
470
471
472
473
      double_format_.clear();
    else
      double_format_ = str;
  }


474
475
476
  void print(double _d)
  {
    char buffer[64];
477
    sprintf_s(buffer, sizeof(buffer), double_format(), _d);
478
479
480
    print(buffer);
  }

481
  void print(const Command& _co)
482
  {
483
484
    switch (_co.com())
    {
485
    case Command::END :
486
      if (is_html()) print_direct("</FONT>");
487
      break;
488
    case Command::END_ERR :
489
490
491
    // Powerdown DEB_error font
    // if (is_html()) print_direct("</BLINK>");
    // fall through
492
    case Command::END_LF :
493
      if (is_html()) print_direct("</FONT>");
494
495
496
497
498
499
500
501
502
      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";

503
504
505
506
      break;
    }
  }

507
  Stream& stream()
508
509
510
  {
    return *deb_stream_;
  }
511

512
513
  // Append current asctime to given string
  bool add_time(std::string& str)
514
515
  {
    time_t rawtime;
516
    time(&rawtime);
517
    struct tm timeinfo;
518
    errno_t err = localtime_s(&timeinfo, &rawtime);
519
520
521
522
523
524
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
525
        str.append(buffer);
526
527
528
529
530
531
        return true;
      }
    }
    return false;
  }

532

533
#if 1
534
  bool hover(std::string& _str, const std::string& _hover, const bool _open)
535
536
537
538
  {
    if (is_html())
    {
      char buffer[1024];
539
540
      if (_open)  sprintf_s(buffer, sizeof(buffer),
                              "<span title=\"%s\">", _hover.c_str());
541
542
543
544
545
546
547
548
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

549
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
550
551
552
553
554
555
556
557
558
559
560
561
  {
    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;
  }

562
  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
563
564
565
566
567
568
569
570
571
572
  {
    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),
573
574
575
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
576
577
578
579
580
581
582
583
584
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


585
586
587
  void header(std::string& str)
  {
    if (is_html())
588
589
590
591
592
593
594
595
596
    {
      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>");
597
      if (is_white_on_black())
598
      {
599
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
600
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
601
602
603
604
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
605
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
606
607
608
609
610
611
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
612
      if (!file_name_.empty())
613
614
615
616
      {
        str.append(file_name_);
        str.append(" opened ");
      }
617
      add_time(str);
618
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
619
620
      if (is_html()) str.append("<BR>");
      str.append("\n");
621
    }
622
   }
623
624
625
626
627
628

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
629
      std::string str("\n");
630
      if (!file_name_.empty()) str.append(file_name_);
631
      str.append(" Closed: ");
632
633
      add_time(str);
      stream() << str << "\n";
634
635
636
    }

    if (is_html())
637
      stream().print("\n</BODY></HTML>", false);
638
  }
639

640
641
642
643
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
644

645
646
647
  int flush()
  {
    int res = 0;
648
    if (!current_.empty())
649
    {
650
651
      const char* fname = file_name();
      if ((fname != nullptr) || file_is_open())
652
      {
653
        if (!file_is_open())
654
        {
655
656
657
          file_stream_.open(fname,
                            std::fstream::out | ((is_appended() && !is_first_flush()) ?
                                                 std::fstream::app  :  std::fstream::trunc));
658
659
660
661
662
        }

        if (file_stream_.is_open())
        {
          std::string hdr;
663
664
665
          if (!is_appended())
          {
            // Reoutput entire file
666
667
668
669
670
671
            header(hdr);
            output_.append(hdr);
            file_stream_ << output_;
          }
          else
          {
672
            if (is_first_flush())
673
674
            {
              header(hdr);
675
676
              if (is_retained())
                output_.append(hdr);
677
678
679
680
681
682
              file_stream_ << hdr;
            }
            ++num_flush_;
          }
          file_stream_ << current_;

683
          if (is_retained())
684
685
            output_.append(current_);

686
687
          current_.clear();
          if (!is_kept_open())
688
689
690
691
692
693
            file_stream_.close();
        } // endif fs.is_open
        else
          res = -1;
      }
    } // endif current empty
694
    return res;
695
696
  }  // endfunc flush

697
698
699
700
  // Use with extreme caution.
  const char * string_out() const
 { return current_.c_str(); }

701
702
703
704
705
706
  void close()
  {
    footer();
    flush();
  }

707
  void set_file_name(const char* _name)
708
  {
709
    file_name_ = _name ? _name : "";
710
    if (is_html_filename(_name))
711
      type_ = (Stream::StreamType)(type_ | Stream::StreamType::HTML);
712
  }
713

714
  int permission(const char* const _flnm)
715
  {
716
717
    int lev = lev_;
    for (const auto& fltrs : level_selc_map_)
718
    {
719
720
721
722
723
724
      if (fltrs.second.select_file(_flnm) ||
          fltrs.second.select_function(call_stack_.call()->name()))
      {
        lev = fltrs.first;
        break;
      }
725
    }
726
727
    return lev;
  }
728
729

  bool is_at_line_start()
730
  {
731
    return at_line_start_;
732
733
  }

734
735
736
  File(
    Stream* _deb_stream,
    Stream::StreamType _type = (Stream::StreamType)(Stream::APPEND | Stream::RETAIN),
737
738
    const char* _file_name = nullptr) :
    type_(_type), lev_(5), deb_stream_(_deb_stream), num_flush_(0)
739
  {
740
    read_debug_config();
741
    set_file_name(_file_name);
742
    indent_size_ = 3;
743
    at_line_start_ = false; // Don't want to indent header
744
  }
745

746
747
748
749
750
751
752
753
754
755
756
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
  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);
    }
795
  }
796

797
}; // endclass File
798

799
800
801
802
803
804
805
806
807
808
809
//////////////////////////////////////////////////////////////////////////

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!
  nmbr_ = _nmbr++; 

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

811
  static int id_cnt = 0;
812
  id_ = ++id_cnt;
813
814

  stream(0, false).dfile()->call_stack().add(_fnct, this);
815
816
}

817
Enter::~Enter()
818
{
819
  File* impl = stream(0, false).dfile();
820
  impl->call_stack().pop();
821

822
  std::string str;
823
  if (((outs_ > 0) || (lns_ > 0)) && impl->anchor(str, id_, "exit", true))
824
  {
825
    impl->anchor(str, id_, "exit", false);
826
827
    impl->print_direct(str);
  }
828
829
}

830
Stream& Enter::stream(const int _warn, const bool _print)
831
{
832
833
  Stream& ds = Stream::get_global(_warn);
  File* impl = ds.dfile();
834

835
  if (_print)
836
  {
837
838
    if (impl->is_html())
    {
839
      // bool is_deb_error = (_warn == 2);
840
      // DEB_error font powerup goes here. BLINK is deprecated sadly.
841
      // if (is_deb_error)  impl->print_direct("<BLINK>");
842
      const int col = 0xFF0000; // RED
843
      char buffer[256];
844
845
      sprintf_s(buffer, sizeof(buffer), "<FONT COLOR=\"#%06X\" SIZE=%i>",
                col, impl->priority() + 1);
846
      impl->print_direct(buffer);
847
848
    }

849
    if (outs_ < 1)
850
851
    {
      // First DEB_out in this function so output callstack, and flush.
852
      impl->indent(true);
853
854
855
856
      std::string str;
      bool is_html = impl->is_html();
      if (is_html)
      {
857
        str.append("<FONT SIZE=2><u>");
858
        impl->anchor(str, id_, "enter", true);
859
      }
860
      else
861
862
      {
        // .txt call stack lead in
863
864
865
        str.append("****>");
      }

866
      impl->call_stack().get(str);
867
      if (is_html) str.append("</u></FONT>");
868
869
      impl->print_direct(str.c_str()); // Don't fork callstack to cerr etc.
      impl->line_break();
870
      ds.dfile()->flush();
871
    }
872
    ++outs_;
873
874
875
876
877
  }

  return ds;
}

878
void FunctionCallSequence::get_indent(std::string& _str, File* _dfile, bool _is_html)
879
880
{
  int num = number_calls();
881
882
  for (int i = 0; i < num; ++i)
  {
883
    Enter* deb = debs_[i];
884
885
886
887
888
889
890
    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];
891
      sprintf_s(hovert, sizeof(hovert), "%s[%i]", func_name_.c_str(), deb->nmbr_);
892
      int col = 0xFFFFFF;
893
894
895
896
897
898
899
900
      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);
901
      if ((deb->outs_ > 0) && _dfile->link_to(_str, deb->id_, "enter", cstk, true))
902
903
904
905
906
907
      {
        _str.append("&lt;");
        _dfile->link_to(_str, deb->id_, "enter", cstk,  false);
      }
      else _str.append("&lt;");

908
      _str.append(deb->flnm_, 1);
909
910
911
912
913

      if (_dfile->link_to(_str, deb->id_, "exit", cstk, true))
      {
        _str.append("&gt;");
        _dfile->link_to(_str, deb->id_, "exit", cstk,  false);
914
        ++deb->lns_;
915
916
917
918
919
920
921
922
923
924
      }

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

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

925
bool CallStack::get_indent(std::string& _str, File* _dfile, const bool is_html)
926
927
928
929
930
931
932
933
934
935
{

  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();
936
937
  int i0 = 0;
  if (!is_html) ++i0; // Don't waste whitespace on first level indent if .txt
938
  for (int i = i0; i < num; ++i)
939
    calls_[i].get_indent(_str, _dfile,  is_html);
940
941
942
943
944
  if (is_html) _str.append(":&nbsp;</FONT>\n");
  return true;
}


945
// =====================================
946
//        Stream member funcs
947
948
949
// =====================================


950
Stream::Stream(const char* _file_name, StreamType _type)
951
952
{
  // NB. disable DEB_out over this new if new contains DEB_out
953
  dfile_ = new File(this, _type, _file_name);
954
955
}

956
Stream::~Stream()
957
{
958
  if (dfile_ != nullptr)
959
  {
960
961
    dfile()->close();
    dfile()->clear();
962
    // NB. disable DEB_out over this delete if delete contains DEB_out
963
964
    delete dfile_;
    dfile_ = nullptr;
965
966
967
  }
}

968
const char * Stream::string_out() const 
969
970
{ return dfile()->string_out();}

971
Stream& Stream::print(const int _i)
972
{
973
  dfile_->print(_i);
974
975
976
  return *this;
};

977
Stream& Stream::print(const double _d)
978
{
979
  dfile_->print(_d);
980
981
982
  return *this;
};

983
Stream& Stream::print(const char* _s, bool _fork)
984
{
985
  dfile_->print(_s, _fork);
986
987
988
  return *this;
};

989
Stream& Stream::print(const char _c)
990
{
991
992
993
994
  dfile_->print(_c);
  return *this;
};

995
Stream& Stream::print(const Command& _co)
996
997
{
  dfile_->print(_co);
998
999
1000
  return *this;
};

1001
Stream& Stream::get_global(int _warn)
1002
{
1003
  _warn;
1004
  // TODO: Replace with a Singleton?? ThreadArray??
1005
  static Stream g_ds__("reform_deb_out.txt");
1006
1007
1008
  return g_ds__;
}

1009
Stream& operator<<(Stream& _ds, const int _i)
1010
1011
1012
{
  return _ds.print(_i);
}
1013
Stream& operator<<(Stream& _ds, const double _d)
1014
1015
1016
{
  return _ds.print(_d);
}
1017
Stream& operator<<(Stream& _ds, const char* const _s)
1018
1019
1020
{
  return _ds.print(_s);
}
1021
Stream& operator<<(Stream& _ds, const char _c)
1022
1023
1024
1025
{
  return _ds.print(_c);
}

1026
Stream& operator<<(Stream& _ds, const size_t _i)
1027
{
1028
  return _ds.print((int)_i);
1029
}
1030
Stream& operator<<(Stream& _ds, const unsigned int _i)
1031
{
Ian Bell's avatar