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

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

#ifdef DEB_ON

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

27 28
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 83 84 85
  int count(int i = 0) const
  {
    int num = number_calls();
    if (i < num) return debs_[num - 1 - i]->count_;
    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 124 125
      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)
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 int _lev, const int _warn, const char* const _flnm)
715
  {
716 717 718
    _warn;
    int lev = lev_;
    for (const auto& fltrs : level_selc_map_)
719
    {
720 721 722 723 724 725
      if (fltrs.second.select_file(_flnm) ||
          fltrs.second.select_function(call_stack_.call()->name()))
      {
        lev = fltrs.first;
        break;
      }
726 727
    }

728
    lev -= _lev;
729 730 731 732
    if (lev > 0)
      priority_ = lev;
    else if (lev < 0)
      lev = 0;
733 734
    return lev;
  }
735 736

  bool is_at_line_start()
737
  {
738
    return at_line_start_;
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
    read_debug_config();
748
    set_file_name(_file_name);
749
    indent_size_ = 3;
750
    at_line_start_ = false; // Don't want to indent header
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 795 796 797 798 799 800 801
  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);
    }
802
  }
803

804
}; // endclass File
805 806

// =====================================
807
//      Enter member funcs
808 809
// =====================================

810
Enter::Enter(const char* const _flnm, const char* const _funcname, const int _count) :
811
  flnm_(_flnm), deb_outs_(0), deb_lines_(0), count_(_count)
812
{
813
  // TODO: this might have to be atomic
814
  static int id_cnt = 0;
815
  id_ = ++id_cnt;
816
  stream(0, false).dfile()->call_stack().add(_funcname, this);
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 (((deb_outs_ > 0) || (deb_lines_ > 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 (deb_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 875 876 877 878 879
    }
    ++deb_outs_;
  }

  return ds;
}

880
int Enter::permission(const int _lev, const int _warn)
881
{
882
  int res = Stream::get_global().dfile()->permission(_lev, _warn, flnm_);
883
  return res;
884 885
}

886

887
void FunctionCallSequence::get_indent(std::string& _str, File* _dfile, bool _is_html)
888 889
{
  int num = number_calls();
890 891
  for (int i = 0; i < num; ++i)
  {
892
    Enter* deb = debs_[i];
893 894 895 896 897 898 899 900
    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_);
901
      int col = 0xFFFFFF;
902 903 904 905 906 907 908 909
      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);
910
      if ((deb->deb_outs_ > 0) && _dfile->link_to(_str, deb->id_, "enter", cstk, true))
911 912 913 914 915 916
      {
        _str.append("&lt;");
        _dfile->link_to(_str, deb->id_, "enter", cstk,  false);
      }
      else _str.append("&lt;");

917
      _str.append(deb->flnm_, 1);
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933

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

934
bool CallStack::get_indent(std::string& _str, File* _dfile, const bool is_html)
935 936 937 938 939 940 941 942 943 944
{

  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();
945 946
  int i0 = 0;
  if (!is_html) ++i0; // Don't waste whitespace on first level indent if .txt
947
  for (int i = i0; i < num; ++i)
948
    calls_[i].get_indent(_str, _dfile,  is_html);
949 950 951 952 953
  if (is_html) _str.append(":&nbsp;</FONT>\n");
  return true;
}


954
// =====================================
955
//        Stream member funcs
956 957 958
// =====================================


959
Stream::Stream(const char* _file_name, StreamType _type)
960 961
{
  // NB. disable DEB_out over this new if new contains DEB_out
962
  dfile_ = new File(this, _type, _file_name);
963 964
}

965
Stream::~Stream()
966
{
967
  if (dfile_ != nullptr)
968
  {
969 970
    dfile()->close();
    dfile()->clear();
971
    // NB. disable DEB_out over this delete if delete contains DEB_out