DebStream.cc 24.1 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
#include "Base/Utils/Environment.hh"
14
15
16

#ifdef DEB_ON

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

#ifndef WIN32
  #define sprintf_s snprintf
#endif
33

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

45
namespace Debug {
46
47

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

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

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

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

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

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

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

99
100
101
102
103
104
  // 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'))
105
    {
106
107
108
109
      char c = *ptr;
      if (c == '>') --cnt;
      if (cnt == 0) str.append(1, c);
      if (c == '<')
110
      {
111
112
        if (cnt == 0) str.append(".");
        ++cnt;
113
      }
114
      ++ptr;
115
    }
116
  }
117

118
119
120
121
122
123
124
  // 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)
125
    {
126
127
128
129
130
      int num = number_calls();
      int prev = -2;
      int seq_cnt = 0;
      for (int i = 0; i < num; ++i)
      {
131
        int cnt = debs_[i]->nmbr_;
132
        if (cnt != prev + 1)
133
        {
134
          char buffer[64];
135

136
137
138
139
          if (seq_cnt > 0)
          {
            _str.append("-");
            sprintf_s(buffer, sizeof(buffer), "%i", prev);
140
            _str.append(buffer);
141
          }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
          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
156

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

159
}; // endclass FunctionCallSequence
160
161


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

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

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

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

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

204
205
206
207
208
209
  // 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)
210
    {
211
212
      if (i > 0) _str.append("->");
      calls_[i].get(_str, true, _with_counts);
213
    }
214
215
    return num;
  }
216

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

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

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

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  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.
  };
275

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

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

284
285
  bool at_line_start_;

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

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

public:

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

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

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

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


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

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

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

368

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

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

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

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

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

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

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


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

414
      if (_c == -62 )  // -62
415
416
        return;

417
      if (_c == -89) // 167 = -89
418
419
420
421
422
      {
        current_.append("&sect;");
        return;
      }

423
      if (_c == -80) // -80
424
425
426
427
428
429
      {
        current_.append("&deg;");
        return;
      }
    }
    current_.append(&_c, 1);
430
431
  }

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

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

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

464

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

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


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

488
  void print(const Command& _co)
489
  {
490
491
    switch (_co.com())
    {
492
    case Command::END :
493
      if (is_html()) print_direct("</FONT>");
494
      break;
495
    case Command::END_ERR :
496
497
498
    // Powerdown DEB_error font
    // if (is_html()) print_direct("</BLINK>");
    // fall through
499
    case Command::END_LF :
500
      if (is_html()) print_direct("</FONT>");
501
502
503
504
505
506
507
508
509
      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";

510
511
512
513
      break;
    }
  }

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

519
520
  // Append current asctime to given string
  bool add_time(std::string& str)
521
522
  {
    time_t rawtime;
523
    time(&rawtime);
524
    struct tm timeinfo;
525
526
#ifdef WIN32
    int err = localtime_s(&timeinfo, &rawtime);
527
528
529
530
531
532
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
533
        str.append(buffer);
534
535
536
        return true;
      }
    }
537
538
539
540
541
542
#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
543
544
545
    return false;
  }

546

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

563
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
564
565
566
567
568
569
570
571
572
573
574
575
  {
    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;
  }

576
  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
577
578
579
580
581
582
583
584
585
586
  {
    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),
587
588
589
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
590
591
592
593
594
595
596
597
598
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


599
600
601
  void header(std::string& str)
  {
    if (is_html())
602
603
604
605
606
607
608
609
610
    {
      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>");
611
      if (is_white_on_black())
612
      {
613
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
614
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
615
616
617
618
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
619
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
620
621
622
623
624
625
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
626
      if (!file_name_.empty())
627
628
629
630
      {
        str.append(file_name_);
        str.append(" opened ");
      }
631
      add_time(str);
632
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
633
634
      if (is_html()) str.append("<BR>");
      str.append("\n");
635
    }
636
   }
637
638
639
640
641
642

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

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

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

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

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

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

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

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

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

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

728
  int permission(const char* const _flnm)
729
  {
730
731
    int lev = lev_;
    for (const auto& fltrs : level_selc_map_)
732
    {
733
734
      if (fltrs.second.select_file(_flnm) ||
          fltrs.second.select_function(call_stack_.call()->name()))
735
736
737
      {// continue this iteration until the maximum allowed level if found
        if (lev < fltrs.first) 
          lev = fltrs.first;
738
      }
739
    }
740
741
    return lev;
  }
742
743

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

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

760
761
  void read_debug_config()
  {
762
763
764
    const auto flnm = 
      System::Environment::variable("REFORM_DEB_CONFIG", "reform_deb.cfg");

765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
    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;
782
783
      //if (lev < 0 || lev > 15)
      //  continue;
784
785
786
787
788
789
790
791
792
793
794
795
796
      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);
    }
797
  }
798

799
}; // endclass File
800

801
802
803
804
805
806
//////////////////////////////////////////////////////////////////////////

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!
807
808
  stream(0, false).dfile()->call_stack().add(_fnct, this);

809
810
811
812
813
  nmbr_ = _nmbr++; 

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

815
  static int id_cnt = 0;
816
  id_ = ++id_cnt;
817
818
}

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

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

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

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

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

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

  return ds;
}

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

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

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

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

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

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

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


947
// =====================================
948
//        Stream member funcs
949
950
951
// =====================================


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

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

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

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

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

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

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

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

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

1011
Stream& operator<<(Stream& _ds, const int _i)
1012
1013
1014
{
  return _ds.print(_i);
}
1015
Stream& operator<<(Stream& _ds, const double _d)
Ian Bell's avatar