DebStream.cc 24.3 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 29 30 31
#include <cstring>
#include <time.h>

#ifndef WIN32
  #define sprintf_s snprintf
#endif
32

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

44
namespace Debug {
45 46

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

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

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

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

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

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

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

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

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

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

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

158
}; // endclass FunctionCallSequence
159 160


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

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

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

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

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

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

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

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

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

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

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

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

283 284
  bool at_line_start_;

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

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

public:

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

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

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

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


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

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

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

367

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

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

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

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

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

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

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


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

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

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

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

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

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

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

463

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

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


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

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

509 510 511 512
      break;
    }
  }

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

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

545

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

747 748 749
  File(
    Stream* _deb_stream,
    Stream::StreamType _type = (Stream::StreamType)(Stream::APPEND | Stream::RETAIN),
750 751
    const char* _file_name = nullptr) :
    type_(_type), lev_(5), deb_stream_(_deb_stream), num_flush_(0)
752
  {
753
    read_debug_config();
754
    set_file_name(_file_name);
755
    indent_size_ = 3;
756
    at_line_start_ = false; // Don't want to indent header
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
  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;
793 794
      //if (lev < 0 || lev > 15)
      //  continue;
795 796 797 798 799 800 801 802 803 804 805 806 807
      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);
    }
808
  }
809

810
}; // endclass File
811

812 813 814 815 816 817
//////////////////////////////////////////////////////////////////////////

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

820 821 822 823 824
  nmbr_ = _nmbr++; 

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

826
  static int id_cnt = 0;
827
  id_ = ++id_cnt;
828 829
}

830
Enter::~Enter()
831
{
832
  File* impl = stream(0, false).dfile();
833
  impl->call_stack().pop();
834

835
  std::string str;
836
  if (((outs_ > 0) || (lns_ > 0)) && impl->anchor(str, id_, "exit", true))
837
  {
838
    impl->anchor(str, id_, "exit", false);
839 840
    impl->print_direct(str);
  }
841 842
}

843
Stream& Enter::stream(const int _warn, const bool _print)
844
{
845 846
  Stream& ds = Stream::get_global(_warn);
  File* impl = ds.dfile();
847

848
  if (_print)
849
  {
850 851
    if (impl->is_html())
    {
852
      // bool is_deb_error = (_warn == 2);
853
      // DEB_error font powerup goes here. BLINK is deprecated sadly.
854
      // if (is_deb_error)  impl->print_direct("<BLINK>");
855
      const int col = 0xFF0000; // RED
856
      char buffer[256];
857 858
      sprintf_s(buffer, sizeof(buffer), "<FONT COLOR=\"#%06X\" SIZE=%i>",
                col, impl->priority() + 1);
859
      impl->print_direct(buffer);
860 861
    }

862
    if (outs_ < 1)
863 864
    {
      // First DEB_out in this function so output callstack, and flush.
865
      impl->indent(true);
866 867 868 869
      std::string str;
      bool is_html = impl->is_html();
      if (is_html)
      {
870
        str.append("<FONT SIZE=2><u>");
871
        impl->anchor(str, id_, "enter", true);
872
      }
873
      else
874 875
      {
        // .txt call stack lead in
876 877 878
        str.append("****>");
      }

879
      impl->call_stack().get(str);
880
      if (is_html) str.append("</u></FONT>");
881 882
      impl->print_direct(str.c_str()); // Don't fork callstack to cerr etc.
      impl->line_break();
883
      ds.dfile()->flush();
884
    }
885
    ++outs_;
886 887 888 889 890
  }

  return ds;
}

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

921
      _str.append(deb->flnm_, 1);
922 923 924 925 926

      if (_dfile->link_to(_str, deb->id_, "exit", cstk, true))
      {
        _str.append("&gt;");
        _dfile->link_to(_str, deb->id_, "exit", cstk,  false);
927
        ++deb->lns_;
928 929 930 931 932 933 934 935 936 937
      }

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

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

938
bool CallStack::get_indent(std::string& _str, File* _dfile, const bool is_html)
939 940 941 942 943 944 945 946 947 948
{

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


958
// =====================================
959
//        Stream member funcs
960 961 962
// =====================================


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