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

34
namespace Debug {
35 36

class FunctionCallSequence
37
{
38
  std::string func_name_;
39
  std::vector<Enter*> debs_;  // These may not have sequential counts when multithreaded.
40 41

public:
42
  FunctionCallSequence(const char* _func_name, Enter* _deb)
43
    : func_name_(_func_name)
44
  {
45 46 47
    debs_.push_back(_deb);
  }
  ~FunctionCallSequence() {}
48

49
  bool add(const char* _func_name, Enter* _deb)
50 51
  {
    if (func_name_ == _func_name)
52
    {
53
      debs_.push_back(_deb);
54
      return true;
55
    }
56 57
    return false;
  }
58

59 60 61
  bool pop()
  {
    if (debs_.size() > 1)
62
    {
63 64
      debs_.pop_back();
      return true;
65
    }
66 67 68
    debs_.clear();
    return false;
  }
69

70 71 72 73 74
  int number_calls() const
  {
    if (!this) return 0;
    return (int)debs_.size();
  }
75

76 77 78 79 80 81
  int count(int i = 0) const
  {
    int num = number_calls();
    if (i < num) return debs_[num - 1 - i]->count_;
    return -1;
  }
82

83 84 85 86
  const char* name() const
  {
    return func_name_.c_str();
  }
87

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

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

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

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

148
}; // endclass FunctionCallSequence
149 150


151 152 153 154 155 156 157
class CallStack
{
  std::vector<FunctionCallSequence> calls_;
  int depth_;
public:
  CallStack() : depth_(0) {}
  ~CallStack() {}
158

159
  void add(const char* _func_name, Enter* _deb)
160 161 162 163 164
  {
    if (calls_.empty() || !calls_.back().add(_func_name, _deb))
      calls_.push_back(FunctionCallSequence(_func_name, _deb));
    ++depth_;
  }
165

166 167 168 169 170 171
  void pop()
  {
    if (!calls_.back().pop())
      calls_.pop_back();
    --depth_;
  }
172

173 174 175 176 177 178
  const FunctionCallSequence* call(int _up = 0)
  {
    int num = (int)calls_.size();
    if (_up < num) return &calls_[num - 1 - _up];
    return nullptr;
  }
179

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

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

206 207 208 209
  int depth() const
  {
    return depth_;
  }
210

211
  bool get_indent(std::string& _str, File* _dfile, const bool is_html);
212
}; // endclass CallStack
213

214 215


216
class File
217 218
{
private:
219 220 221 222 223
  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
224
    module_stats(int _lev = 0) : lev_(_lev), col_(0x808080) {}
225 226 227 228 229 230
    ~module_stats() {}
  };

  std::map<std::string, module_stats> module_map_;
  typedef std::map<std::string, module_stats>::iterator module_map_itr;
  typedef std::map<std::string, module_stats>::const_iterator const_module_map_itr;
231

232
  Stream::StreamType type_;
233 234
  int lev_;
  int num_flush_;
235
  int priority_; // Last permission granted
236
  int indent_size_;
237

238 239
  bool at_line_start_;

240 241 242 243
  std::string current_;
  std::string output_;
  std::string file_name_;
  std::fstream file_stream_;
244 245

  std::string double_format_;
246
  //std::string indent_string_;
247
  Stream* deb_stream_;
248 249 250 251
  CallStack call_stack_;

public:

252 253 254 255
  CallStack& call_stack()
  {
    return call_stack_;
  }
256

257 258
  bool is_kept_open() const
  {
259
    return 0 != (type_ & Stream::StreamType::KEEP_OPEN);
260 261 262
  }
  bool is_html() const
  {
263
    return 0 != (type_ & Stream::StreamType::HTML);
264 265 266
  }
  bool is_retained() const
  {
267
    return 0 != (type_ & Stream::StreamType::RETAIN);
268 269 270
  }
  bool is_appended() const
  {
271
    return 0 != (type_ & Stream::StreamType::APPEND);
272
  }
273
  // Only applies to HTML DEB_out
274 275 276 277 278 279 280 281
  bool is_white_on_black() const
  {
    return true;
  }
  int indent_size()
  {
    return indent_size_;
  }
282

283 284 285 286 287 288 289 290
  bool file_is_open() const
  {
    return file_stream_.is_open();
  }
  int priority() const
  {
    return priority_;
  }
291

292 293 294 295 296 297 298 299
  bool fork_to_cout()
  {
    return false;
  }
  bool fork_to_cerr()
  {
    return true;
  }
300 301


302
  const char* file_name() const
303
  {
304
    if (this && (!file_name_.empty())) return file_name_.c_str();
305 306
    return nullptr;
  }
307

308 309 310 311 312 313
  void clear()
  {
    current_.clear();
    output_.clear();
    file_name_.clear();
  }
314

315 316 317 318 319 320 321
  char prev_char() const
  {
    if (!current_.empty()) return current_.back();
    if (!output_.empty()) return output_.back();
    return '\0';
  }

322

323
  void indent(bool _full_text)
324
  {
325
    std::string str;
326
    if (call_stack().get_indent(str, this,  _full_text && is_html()))
327
      current_.append(str);
328 329
  }

330

331
  void line_break(bool _with_indent = false)
332
  {
333
    if (is_html()) current_.append("<br>");   // Don't bother with matching </br>
334 335
    current_.append("\n", 1);

336 337
    if (_with_indent) indent(false);
    else at_line_start_ = true;
338

339 340
  }

341 342 343 344 345 346
  void set_level(int _lev)
  {
    lev_ = _lev;
  }

  void print_direct(const std::string& _s)
347 348 349 350 351
  {
    current_.append(_s);
  }


352 353
  void print(const char _c)
  {
354
    if (_c == '\n')
355 356 357 358 359
    {
      line_break();
      return;
    }

360 361
    if (at_line_start_)
    {
362
      indent(true);
363 364 365 366
      at_line_start_ = false;
    }


367 368 369
    if (is_html())
    {
      // translate the esoteric characters used in IGM DEB_out
370

371
      if (_c == '')  // -62
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
        return;

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

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

389 390 391 392 393 394

  void print_to_ostream(const char* const _s, std::ostream& os)
  {
    os << _s;
  }

395
  void print(const char* const _s, bool _fork = true)
396 397 398
  {
    if (_s != nullptr)
    {
399
      for (int i = 0; ; ++i)
400 401
      {
        const char c = _s[i];
402
        if (c == '\0') break;
403 404 405 406
        print(c);
      }
      if (_fork)
      {
407
        if (fork_to_cout())
408
          print_to_ostream(_s, std::cout);
409
        if (fork_to_cerr())
410
          print_to_ostream(_s, std::cerr);
411
      }
412
    }
413 414 415 416 417 418 419 420
  }

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

422

423
  const char* double_format() const
424 425 426 427 428 429
  {
    if (double_format_.empty())
      return "%.17g";
    return double_format_.c_str();
  }

430
  void set_double_format(const char* const str)
431
  {
432
    if (str == nullptr)
433 434 435 436 437 438
      double_format_.clear();
    else
      double_format_ = str;
  }


439 440 441
  void print(double _d)
  {
    char buffer[64];
442
    sprintf_s(buffer, sizeof(buffer), double_format(), _d);
443 444 445
    print(buffer);
  }

446
  void print(const Command& _co)
447
  {
448 449
    switch (_co.com())
    {
450
    case Command::END :
451
      if (is_html()) print_direct("</FONT>");
452
      break;
453
    case Command::END_ERR :
454 455 456
    // Powerdown DEB_error font
    // if (is_html()) print_direct("</BLINK>");
    // fall through
457
    case Command::END_LF :
458
      if (is_html()) print_direct("</FONT>");
459 460 461 462 463 464 465 466 467
      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";

468 469 470 471
      break;
    }
  }

472
  Stream& stream()
473 474 475
  {
    return *deb_stream_;
  }
476

477 478
  // Append current asctime to given string
  bool add_time(std::string& str)
479 480
  {
    time_t rawtime;
481
    time(&rawtime);
482
    struct tm timeinfo;
483
    errno_t err = localtime_s(&timeinfo, &rawtime);
484 485 486 487 488 489
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
490
        str.append(buffer);
491 492 493 494 495 496
        return true;
      }
    }
    return false;
  }

497

498
#if 1
499
  bool hover(std::string& _str, const std::string& _hover, const bool _open)
500 501 502 503
  {
    if (is_html())
    {
      char buffer[1024];
504 505
      if (_open)  sprintf_s(buffer, sizeof(buffer),
                              "<span title=\"%s\">", _hover.c_str());
506 507 508 509 510 511 512 513
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

514
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
515 516 517 518 519 520 521 522 523 524 525 526
  {
    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;
  }

527
  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
528 529 530 531 532 533 534 535 536 537
  {
    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),
538 539 540
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
541 542 543 544 545 546 547 548 549
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


550 551 552
  void header(std::string& str)
  {
    if (is_html())
553 554 555 556 557 558 559 560 561
    {
      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>");
562
      if (is_white_on_black())
563
      {
564
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
565
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
566 567 568 569
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
570
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
571 572 573 574 575 576
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
577
      if (!file_name_.empty())
578 579 580 581
      {
        str.append(file_name_);
        str.append(" opened ");
      }
582
      add_time(str);
583
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
584 585 586 587 588
#ifdef DEB_SHOW_ON
      // Add link in deb_out.htm to deb_show.htm
      if (is_html())
        str.append(" :: <A HREF=\"ReForm_deb_show.htm\">ReForm_deb_show.htm</A><BR>\n");
#endif
589 590
      if (is_html()) str.append("<BR>");
      str.append("\n");
591
    }
592
   }
593 594 595 596 597 598

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
599
      std::string str("\n");
600
      if (!file_name_.empty()) str.append(file_name_);
601
      str.append(" Closed: ");
602 603
      add_time(str);
      stream() << str << "\n";
604 605 606
    }

    if (is_html())
607
      stream().print("\n</BODY></HTML>", false);
608
  }
609

610 611 612 613
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
614

615 616 617
  int flush()
  {
    int res = 0;
618
    if (!current_.empty())
619
    {
620 621
      const char* fname = file_name();
      if ((fname != nullptr) || file_is_open())
622
      {
623
        if (!file_is_open())
624
        {
625 626 627
          file_stream_.open(fname,
                            std::fstream::out | ((is_appended() && !is_first_flush()) ?
                                                 std::fstream::app  :  std::fstream::trunc));
628 629 630 631 632
        }

        if (file_stream_.is_open())
        {
          std::string hdr;
633 634 635
          if (!is_appended())
          {
            // Reoutput entire file
636 637 638 639 640 641
            header(hdr);
            output_.append(hdr);
            file_stream_ << output_;
          }
          else
          {
642
            if (is_first_flush())
643 644
            {
              header(hdr);
645 646
              if (is_retained())
                output_.append(hdr);
647 648 649 650 651 652
              file_stream_ << hdr;
            }
            ++num_flush_;
          }
          file_stream_ << current_;

653
          if (is_retained())
654 655
            output_.append(current_);

656 657
          current_.clear();
          if (!is_kept_open())
658 659 660 661 662 663
            file_stream_.close();
        } // endif fs.is_open
        else
          res = -1;
      }
    } // endif current empty
664
    return res;
665 666
  }  // endfunc flush

667 668 669 670
  // Use with extreme caution.
  const char * string_out() const
 { return current_.c_str(); }

671 672 673 674 675 676 677
  void close()
  {
    footer();
    flush();
  }


678
  void set_file_name(const char* _name)
679
  {
680
    file_name_ = _name ? _name : "";
681
    if (is_html_filename(_name))
682
      type_ = (Stream::StreamType)(type_ | Stream::StreamType::HTML);
683
  }
684 685

  void set_module_level(const char* const _module, const int _lev)
686
  {
687 688 689 690 691 692
    if (this)
    {
      std::pair<module_map_itr, bool> ins =  module_map_.insert(
          std::pair<std::string, module_stats>(_module, module_stats(_lev)));
      if (!ins.second) ins.first->second.lev_ = _lev;
    }
693 694
  }

695
  int module_level(const char* const _module) const
696
  {
697
    if (!this) return -1;
698 699 700 701 702
    const_module_map_itr it = module_map_.find(std::string(_module));
    if (it == module_map_.end()) return lev_;
    return it->second.lev_;
  }

703
  void set_module_color(const char* const _module, const int _col)
704
  {
705 706 707 708 709 710 711
    if (this)
    {
      std::pair<module_map_itr, bool> ins =  module_map_.insert(
          std::pair<std::string, module_stats>(_module, module_stats(lev_)));
      ins.first->second.col_ = _col;
    }

712 713
  }

714
  int get_module_color(const char* const _module) const
715 716
  {
    const_module_map_itr it = module_map_.find(std::string(_module));
717 718 719 720 721
    if (it == module_map_.end())
    {
      if (is_white_on_black()) return 0xFFFFFF;
      else return 0x000000;
    }
722 723
    return it->second.col_;
  }
724 725

  int permission(const int _lev, const int _warn, const char* const _module)
726 727
  {
    _warn;
728
    int lev = module_level(_module);
729
    lev -= _lev;
730
    if (lev < 0) lev = 0;
731 732 733
    if (lev > 0) priority_ = lev;
    return lev;
  }
734 735

  bool is_at_line_start()
736
  {
737
    return at_line_start_;
738 739 740
  }


741 742 743
  File(
    Stream* _deb_stream,
    Stream::StreamType _type = (Stream::StreamType)(Stream::APPEND | Stream::RETAIN),
744 745
    const char* _file_name = nullptr) :
    type_(_type), lev_(5), deb_stream_(_deb_stream), num_flush_(0)
746 747
  {
    set_file_name(_file_name);
748
    indent_size_ = 3;
749
    at_line_start_ = false; // Don't want to indent header
750 751
    //    indent_string_ = ".";
    set_module_color("PARA", 0xFF8000);
752 753
    set_module_color("SOLV", 0x00FF00);
    set_module_color("NSLV", 0xFFFF00);
754
    set_module_color("FELD", 0x0080FF);
755
    set_module_color("CURV", 0x00FFFF);
756 757
    set_module_color("QMGN", 0xFF00FF);
    // Don't set a TEST module colour here because Test is not part of Reform
758 759 760 761 762 763 764 765 766 767 768 769

#if 0  // Such custom setting calls really belong in Test
    set_module_level("PARA", 1);
    set_module_level("CFLD", 0);
    set_module_level("NSLV", 0);
    set_module_level("QMGN", 0);
    set_module_level("CURV", 5);
    set_module_level("SOLV", 0);
    set_module_level("FELD", 0);
#endif


770 771
  }

772
  ~File() 
773 774 775
  {
    // The closing of the primary DEB_out file is a good place to flush and close
    // any subsidary DEB files
776
#ifdef DEB_SHOW_ON
777
    if (type_ & Stream::PRIMARY)
778 779 780 781 782 783
    {
      DebShow::DebShowStream os;
      os << file_name_ << " closing";
      DebShow::DEB_show_close(os.string_out());
    }
#endif
784
  }
785

786

787
}; // endclass File
788 789

// =====================================
790
//      Enter member funcs
791 792
// =====================================

793
Enter::Enter(const char* const _funcname, const int _count, const char* const _module)
794
{
795
  // TODO: this might have to be atomic
796
  static int id_cnt = 0;
797
  module_ = _module;
798
  deb_outs_ = 0;
799
  deb_lines_ = 0;
800
  id_ = ++id_cnt;
801 802
  count_ = _count;
  stream(0, false).dfile()->call_stack().add(_funcname, this);
803 804
}

805
Enter::~Enter()
806
{
807
  File* impl = stream(0, false).dfile();
808
  impl->call_stack().pop();
809

810
  std::string str;
811
  if (((deb_outs_ > 0) || (deb_lines_ > 0)) && impl->anchor(str, id_, "exit", true))
812
  {
813
    impl->anchor(str, id_, "exit", false);
814 815
    impl->print_direct(str);
  }
816 817
}

818
Stream& Enter::stream(const int _warn, const bool _print)
819
{
820 821
  Stream& ds = Stream::get_global(_warn);
  File* impl = ds.dfile();
822

823
  if (_print)
824
  {
825 826
    if (impl->is_html())
    {
827
      bool is_deb_error = (_warn == 2);
828
      // DEB_error font powerup goes here. BLINK is deprecated sadly.
829
      // if (is_deb_error)  impl->print_direct("<BLINK>");
830
      int col = impl->get_module_color(module_);
831
      if (is_deb_error) col = 0xFF0000; // RED
832
      char buffer[256];
833 834
      sprintf_s(buffer, sizeof(buffer), "<FONT COLOR=\"#%06X\" SIZE=%i>",
                col, impl->priority() + 1);
835
      impl->print_direct(buffer);
836 837
    }

838
    if (deb_outs_ < 1)
839 840
    {
      // First DEB_out in this function so output callstack, and flush.
841
      impl->indent(true);
842 843 844 845
      std::string str;
      bool is_html = impl->is_html();
      if (is_html)
      {
846
        str.append("<FONT SIZE=2><u>");
847
        impl->anchor(str, id_, "enter", true);
848
      }
849
      else
850 851
      {
        // .txt call stack lead in
852 853 854
        str.append("****>");
      }

855
      impl->call_stack().get(str);
856
      if (is_html) str.append("</u></FONT>");
857 858
      impl->print_direct(str.c_str()); // Don't fork callstack to cerr etc.
      impl->line_break();
859
      ds.dfile()->flush();
860 861 862 863 864 865 866
    }
    ++deb_outs_;
  }

  return ds;
}

867
int Enter::permission(const int _lev, const int _warn)
868
{
869
  int res = Stream::get_global().dfile()->permission(_lev, _warn, module_);
870
  return res;
871 872
}

873

874
void FunctionCallSequence::get_indent(std::string& _str, File* _dfile, bool _is_html)
875 876
{
  int num = number_calls();
877 878
  for (int i = 0; i < num; ++i)
  {
879
    Enter* deb = debs_[i];
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
    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];
      sprintf_s(hovert, sizeof(hovert), "%s[%i]", func_name_.c_str(), deb->count_);
      int col = _dfile->get_module_color(deb->module_);
      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);
897
      if ((deb->deb_outs_ > 0) && _dfile->link_to(_str, deb->id_, "enter", cstk, true))
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
      {
        _str.append("&lt;");
        _dfile->link_to(_str, deb->id_, "enter", cstk,  false);
      }
      else _str.append("&lt;");

      _str.append(deb->module_, 1);

      if (_dfile->link_to(_str, deb->id_, "exit", cstk, true))
      {
        _str.append("&gt;");
        _dfile->link_to(_str, deb->id_, "exit", cstk,  false);
        ++deb->deb_lines_;
      }

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

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

921
bool CallStack::get_indent(std::string& _str, File* _dfile, const bool is_html)
922 923 924 925 926 927 928 929 930 931
{

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


941
// =====================================
942
//        Stream member funcs
943 944 945
// =====================================


946
Stream::Stream(const char* _file_name, StreamType _type)