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();}