DebStream.cc 25.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 "DebDefault.hh"
13
#include "Base/Utils/ThrowError.hh"
14
#include "Base/Utils/Environment.hh"
15
16
#include "Base/Test/IChecksum.hh"
#include "Base/Test/ChecksumCount.hh"
17
18
19

#ifdef DEB_ON

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

#ifndef WIN32
  #define sprintf_s snprintf
#endif
36

37
38
39
namespace {

// TODO: make this use std::string; check for html extension; case insensitive
40
bool is_html_filename(const char* const str)
41
{
42
  if (str == nullptr) return false;
43
  const char* dot = strrchr(str, '.');
44
45
  if (dot == nullptr) return false;
  ++dot;
46
  return (!strncmp(dot, "htm", 3)) || (!strncmp(dot, "HTM", 3)) ;
47
}
48

49
}
50

51
namespace Debug {
52
53

class FunctionCallSequence
54
{
55
  std::string func_name_;
56
  std::vector<Enter*> debs_;  // These may not have sequential counts when multithreaded.
57
58

public:
59
  FunctionCallSequence(const char* _func_name, Enter* _deb)
60
    : func_name_(_func_name)
61
  {
62
63
64
    debs_.push_back(_deb);
  }
  ~FunctionCallSequence() {}
65

66
  bool add(const char* _func_name, Enter* _deb)
67
68
  {
    if (func_name_ == _func_name)
69
    {
70
      debs_.push_back(_deb);
71
      return true;
72
    }
73
74
    return false;
  }
75

76
77
78
  bool pop()
  {
    if (debs_.size() > 1)
79
    {
80
81
      debs_.pop_back();
      return true;
82
    }
83
84
85
    debs_.clear();
    return false;
  }
86

87
88
89
90
91
  int number_calls() const
  {
    if (!this) return 0;
    return (int)debs_.size();
  }
92

93
94
95
  int count(int i = 0) const
  {
    int num = number_calls();
96
    if (i < num) return debs_[num - 1 - i]->nmbr_;
97
98
    return -1;
  }
99

100
101
102
103
  const char* name() const
  {
    return func_name_.c_str();
  }
104

105
106
107
108
109
110
  // 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'))
111
    {
112
113
114
115
      char c = *ptr;
      if (c == '>') --cnt;
      if (cnt == 0) str.append(1, c);
      if (c == '<')
116
      {
117
118
        if (cnt == 0) str.append(".");
        ++cnt;
119
      }
120
      ++ptr;
121
    }
122
  }
123

124
125
126
127
128
129
130
  // 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)
131
    {
132
133
134
135
136
      int num = number_calls();
      int prev = -2;
      int seq_cnt = 0;
      for (int i = 0; i < num; ++i)
      {
137
        int cnt = debs_[i]->nmbr_;
138
        if (cnt != prev + 1)
139
        {
140
          char buffer[64];
141

142
143
144
145
          if (seq_cnt > 0)
          {
            _str.append("-");
            sprintf_s(buffer, sizeof(buffer), "%i", prev);
146
            _str.append(buffer);
147
          }
148
149
150
151
152
153
154
155
156
157
158
159
160
161
          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
162

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

165
}; // endclass FunctionCallSequence
166

167
//////////////////////////////////////////////////////////////////////////
168
169
170
171
172
173
174
class CallStack
{
  std::vector<FunctionCallSequence> calls_;
  int depth_;
public:
  CallStack() : depth_(0) {}
  ~CallStack() {}
175

176
  void add(const char* _func_name, Enter* _deb)
177
178
179
180
181
  {
    if (calls_.empty() || !calls_.back().add(_func_name, _deb))
      calls_.push_back(FunctionCallSequence(_func_name, _deb));
    ++depth_;
  }
182

183
184
185
186
187
188
  void pop()
  {
    if (!calls_.back().pop())
      calls_.pop_back();
    --depth_;
  }
189

190
  const FunctionCallSequence* call(int _up = 0) const
191
192
193
194
195
  {
    int num = (int)calls_.size();
    if (_up < num) return &calls_[num - 1 - _up];
    return nullptr;
  }
196

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

210
211
212
213
214
215
  // 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)
216
    {
217
218
      if (i > 0) _str.append("->");
      calls_[i].get(_str, true, _with_counts);
219
    }
220
221
    return num;
  }
222

223
224
225
226
  int depth() const
  {
    return depth_;
  }
227

228
  bool get_indent(std::string& _str, File* _dfile, const bool is_html);
229
}; // endclass CallStack
230

231
//////////////////////////////////////////////////////////////////////////
232
class File
233
234
{
public:
235
236
237
238
239
240
241
242
243
244
  File(const char* _flnm = nullptr, 
    const uint _flags = Stream::APPEND | Stream::RETAIN) 
    : flags_(_flags), lev_(5), num_flush_(0)
  {
    if (_flnm != nullptr) 
      read_debug_config();// TODO: not sure if this is the right location
    set_filename(_flnm);
    indent_size_ = 3;
    at_line_start_ = false; // Don't want to indent header
  }
245

246
247
248
249
  CallStack& call_stack()
  {
    return call_stack_;
  }
250

251
252
  bool is_kept_open() const
  {
253
    return 0 != (flags_ & Stream::KEEP_OPEN);
254
255
256
  }
  bool is_html() const
  {
257
    return 0 != (flags_ & Stream::HTML);
258
259
260
  }
  bool is_retained() const
  {
261
    return 0 != (flags_ & Stream::RETAIN);
262
263
264
  }
  bool is_appended() const
  {
265
    return 0 != (flags_ & Stream::APPEND);
266
  }
267
  // Only applies to HTML DEB_out
268
269
270
271
272
273
274
275
  bool is_white_on_black() const
  {
    return true;
  }
  int indent_size()
  {
    return indent_size_;
  }
276

277
278
279
280
  bool file_is_open() const
  {
    return file_stream_.is_open();
  }
281

282
283
284
285
  int priority() const
  {
    return priority_;
  }
286

287
288
289
290
  bool fork_to_cout()
  {
    return false;
  }
291

292
293
294
295
  bool fork_to_cerr()
  {
    return true;
  }
296

297
  const char* filename() const
298
  {
299
    if (this && (!flnm_.empty())) return flnm_.c_str();
300
301
    return nullptr;
  }
302

303
304
305
306
  void clear()
  {
    current_.clear();
    output_.clear();
307
    flnm_.clear();
308
  }
309

310
311
312
313
314
315
316
  char prev_char() const
  {
    if (!current_.empty()) return current_.back();
    if (!output_.empty()) return output_.back();
    return '\0';
  }

317
  void indent(bool _full_text)
318
  {
319
    std::string str;
320
    if (call_stack().get_indent(str, this,  _full_text && is_html()))
321
      current_.append(str);
322
323
  }

324
  void line_break(bool _with_indent = false)
325
  {
326
    if (is_html()) current_.append("<br>");   // Don't bother with matching </br>
327
328
    current_.append("\n", 1);

329
330
    if (_with_indent) indent(false);
    else at_line_start_ = true;
331
332
  }

333
334
335
336
337
338
  void set_level(int _lev)
  {
    lev_ = _lev;
  }

  void print_direct(const std::string& _s)
339
340
341
342
  {
    current_.append(_s);
  }

343
344
  void print(const char _c)
  {
345
    if (_c == '\n')
346
347
348
349
350
    {
      line_break();
      return;
    }

351
352
    if (at_line_start_)
    {
353
      indent(true);
354
355
356
357
      at_line_start_ = false;
    }


358
359
360
    if (is_html())
    {
      // translate the esoteric characters used in IGM DEB_out
361

362
      if (_c == -62 )  // -62
363
364
        return;

365
      if (_c == -89) // 167 = -89
366
367
368
369
370
      {
        current_.append("&sect;");
        return;
      }

371
      if (_c == -80) // -80
372
373
374
375
376
377
      {
        current_.append("&deg;");
        return;
      }
    }
    current_.append(&_c, 1);
378
379
  }

380
381
382
383
384
  void print_to_ostream(const char* const _s, std::ostream& os)
  {
    os << _s;
  }

385
  void print(const char* const _s, bool _fork = true)
386
387
388
  {
    if (_s != nullptr)
    {
389
      for (int i = 0; ; ++i)
390
391
      {
        const char c = _s[i];
392
        if (c == '\0') break;
393
394
395
396
        print(c);
      }
      if (_fork)
      {
397
        if (fork_to_cout())
398
          print_to_ostream(_s, std::cout);
399
        if (fork_to_cerr())
400
          print_to_ostream(_s, std::cerr);
401
      }
402
    }
403
404
405
406
407
408
409
410
  }

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

412

413
  const char* double_format() const
414
415
416
417
418
419
  {
    if (double_format_.empty())
      return "%.17g";
    return double_format_.c_str();
  }

420
  void set_double_format(const char* const str)
421
  {
422
    if (str == nullptr)
423
424
425
426
427
428
      double_format_.clear();
    else
      double_format_ = str;
  }


429
430
431
  void print(double _d)
  {
    char buffer[64];
432
    sprintf_s(buffer, sizeof(buffer), double_format(), _d);
433
434
435
    print(buffer);
  }

436
  void print(const Command& _co)
437
  {
438
    switch (_co.cmd)
439
    {
440
    case Command::END :
441
      if (is_html()) print_direct("</FONT>");
442
      break;
443
    case Command::END_ERR :
444
445
446
    // Powerdown DEB_error font
    // if (is_html()) print_direct("</BLINK>");
    // fall through
447
    case Command::END_LF :
448
      if (is_html()) print_direct("</FONT>");
449
450
451
452
453
454
455
456
457
      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";

458
459
460
461
      break;
    }
  }

462
463
  // Append current asctime to given string
  bool add_time(std::string& str)
464
465
  {
    time_t rawtime;
466
    time(&rawtime);
467
    struct tm timeinfo;
468
469
#ifdef WIN32
    int err = localtime_s(&timeinfo, &rawtime);
470
471
472
473
474
475
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
476
        str.append(buffer);
477
478
479
        return true;
      }
    }
480
481
482
483
484
485
#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
486
487
488
    return false;
  }

489

490
#if 1
491
  bool hover(std::string& _str, const std::string& _hover, const bool _open)
492
493
494
495
  {
    if (is_html())
    {
      char buffer[1024];
496
497
      if (_open)  sprintf_s(buffer, sizeof(buffer),
                              "<span title=\"%s\">", _hover.c_str());
498
499
500
501
502
503
504
505
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

506
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
507
508
509
510
511
512
513
514
515
516
517
518
  {
    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;
  }

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


542
543
544
  void header(std::string& str)
  {
    if (is_html())
545
546
547
548
549
550
551
552
553
    {
      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>");
554
      if (is_white_on_black())
555
      {
556
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
557
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
558
559
560
561
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
562
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
563
564
565
566
567
568
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
569
      if (!flnm_.empty())
570
      {
571
        str.append(flnm_);
572
573
        str.append(" opened ");
      }
574
      add_time(str);
575
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
576
577
      if (is_html()) str.append("<BR>");
      str.append("\n");
578
    }
579
   }
580
581
582
583
584
585

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
586
      std::string str("\n");
587
      if (!flnm_.empty()) str.append(flnm_);
588
      str.append(" Closed: ");
589
      add_time(str);
590
591
      str.append("\n");
      print(str.c_str());
592
593
594
    }

    if (is_html())
595
      print("\n</BODY></HTML>", false);
596
  }
597

598
599
600
601
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
602

603
604
605
  int flush()
  {
    int res = 0;
606
    if (!current_.empty())
607
    {
608
      const char* fname = filename();
609
      if ((fname != nullptr) || file_is_open())
610
      {
611
        if (!file_is_open())
612
        {
613
614
615
          file_stream_.open(fname,
                            std::fstream::out | ((is_appended() && !is_first_flush()) ?
                                                 std::fstream::app  :  std::fstream::trunc));
616
617
618
619
620
        }

        if (file_stream_.is_open())
        {
          std::string hdr;
621
622
623
          if (!is_appended())
          {
            // Reoutput entire file
624
625
626
627
628
629
            header(hdr);
            output_.append(hdr);
            file_stream_ << output_;
          }
          else
          {
630
            if (is_first_flush())
631
632
            {
              header(hdr);
633
634
              if (is_retained())
                output_.append(hdr);
635
636
637
638
639
640
              file_stream_ << hdr;
            }
            ++num_flush_;
          }
          file_stream_ << current_;

641
          if (is_retained())
642
643
            output_.append(current_);

644
645
          current_.clear();
          if (!is_kept_open())
646
647
648
649
650
651
            file_stream_.close();
        } // endif fs.is_open
        else
          res = -1;
      }
    } // endif current empty
652
    return res;
653
654
  }  // endfunc flush

655
  // Use with extreme caution.
656
  const std::string& string() const { return current_; }
657

658
659
660
661
662
663
  void close()
  {
    footer();
    flush();
  }

664
  void set_filename(const char* _flnm)
665
  {
666
667
668
    flnm_ = _flnm != nullptr ? _flnm : "";
    if (is_html_filename(_flnm))
      flags_ = flags_ | Stream::HTML;
669
  }
670

671
  int permission(const char* const _flnm)
672
  {
673
674
    int lev = lev_;
    for (const auto& fltrs : level_selc_map_)
675
    {
676
677
      if (fltrs.second.select_file(_flnm) ||
          fltrs.second.select_function(call_stack_.call()->name()))
678
679
680
      {// continue this iteration until the maximum allowed level if found
        if (lev < fltrs.first) 
          lev = fltrs.first;
681
      }
682
    }
683
684
    return lev;
  }
685
686

  bool is_at_line_start()
687
  {
688
    return at_line_start_;
689
690
  }

691
692
  void read_debug_config()
  {
693
694
695
    const auto flnm = 
      System::Environment::variable("REFORM_DEB_CONFIG", "reform_deb.cfg");

696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
    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;
713
714
      //if (lev < 0 || lev > 15)
      //  continue;
715
716
717
718
719
720
721
722
723
724
725
726
727
      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);
    }
728
  }
729

730
731
732
733
734
735
736
737
738
739
740
741
742
743
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
private:
  // 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_);
    }

  private:
    static bool search(const std::string& _flnm,
      const std::list<std::string>& _sel_strings)
    {
      for (const auto& sel : _sel_strings)
      {
        if (_flnm.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.
  };

  uint flags_;
  int lev_;
  int num_flush_;
  int priority_; // Last permission granted
  int indent_size_;

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

  bool at_line_start_;

  std::string current_;
  std::string output_;
  std::string flnm_;
  std::fstream file_stream_;

  std::string double_format_;
  //std::string indent_string_;
  CallStack call_stack_;
789
}; // endclass File
790

791
792
//////////////////////////////////////////////////////////////////////////

793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
namespace {

Stream& global_stream()
{
  // TODO: Replace with a Singleton?? ThreadArray??
  static Stream glbl_strm(Debug::Default::LOG_FILENAME);
  return glbl_strm;
}

// Stream together <__FUNCTION__, __FILE__, __LINE__>
// Cannot use std::tupple, we are not using C++11 in Base
struct TriggerPoint
{
  TriggerPoint(
    const char* _fnct,
    const char* _file,
    const int _line
    ) 
    : fnct(_fnct), file(_file), line(_line) 
  {}
  
  const char* fnct;
  const char* file;
  int line;
};

Stream& operator<<(Stream& _os, const TriggerPoint& _trgr_pnt)
{
#ifdef WIN32
  const char path_sep = '\\';
#else//!WIN32
  const char path_sep = '/';
#endif//WIN32
  
  auto flnm_pntr = strrchr(_trgr_pnt.file, path_sep); 
  if (flnm_pntr == nullptr) 
    flnm_pntr = _trgr_pnt.file;
  else
    ++flnm_pntr;

  return _os << " @ [" << _trgr_pnt.fnct << "() in " 
    << flnm_pntr << ":" << _trgr_pnt.line << "]";
}

#define TRIGGER_POINT TriggerPoint(_fnct, _file, _line)

}//namespace


// put test checksum tag and separators in the required format

void warning(const std::string& _wrng, const char* const _fnct, 
  const char* const _file, const int _line)
{  
  TEST_only(Test::Checksum::checksum_count_add_warning());
  global_stream() << WARNING << ": " << _wrng << TRIGGER_POINT 
    << Command::END_LF;
}

void error(const std::string& _err, const char* const _fnct, 
  const char* const _file, const int _line)
{
  TEST_only(Test::Checksum::checksum_count_add_error());
  global_stream() << ERROR << ": " << _err << TRIGGER_POINT << Command::END_ERR;
}

#undef FUNCTION_IN_FILE

//////////////////////////////////////////////////////////////////////////

863
864
865
866
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!
867
  global_stream().dfile()->call_stack().add(_fnct, this);
868

869
870
871
  nmbr_ = _nmbr++; 

  if (_lvl == INVALID_LEVEL)
872
    _lvl = global_stream().dfile()->permission(flnm_);
873
  lvl_ = _lvl;
874

875
  static int id_cnt = 0;
876
  id_ = ++id_cnt;
877
878
}

879
Enter::~Enter()
880
{
881
  File* impl = global_stream().dfile();
882
  impl->call_stack().pop();
883

884
  std::string str;
885
  if (((outs_ > 0) || (lns_ > 0)) && impl->anchor(str, id_, "exit", true))
886
  {
887
    impl->anchor(str, id_, "exit", false);
888
889
    impl->print_direct(str);
  }
890
891
}

892
Stream& Enter::stream()
893
{
894
  Stream& ds = global_stream();
895
  File* impl = ds.dfile();
896

897
898
899
900
901
902
903
904
905
906
907
908
909
  if (impl->is_html())
  {
    // bool is_deb_error = (_warn == 2);
    // DEB_error font powerup goes here. BLINK is deprecated sadly.
    // if (is_deb_error)  impl->print_direct("<BLINK>");
    const int col = 0xFF0000; // RED
    char buffer[256];
    sprintf_s(buffer, sizeof(buffer), "<FONT COLOR=\"#%06X\" SIZE=%i>",
              col, impl->priority() + 1);
    impl->print_direct(buffer);
  }

  if (outs_ < 1)
910
  {
911
912
913
914
915
    // First DEB_out in this function so output callstack, and flush.
    impl->indent(true);
    std::string str;
    bool is_html = impl->is_html();
    if (is_html)
916
    {
917
918
      str.append("<FONT SIZE=2><u>");
      impl->anchor(str, id_, "enter", true);
919
    }
920
    else
921
    {
922
923
      // .txt call stack lead in
      str.append("****>");
924
    }
925
926
927
928
929
930

    impl->call_stack().get(str);
    if (is_html) str.append("</u></FONT>");
    impl->print_direct(str.c_str()); // Don't fork callstack to cerr etc.
    impl->line_break();
    ds.dfile()->flush();
931
  }
932
  ++outs_;
933
934
935
936

  return ds;
}

937
void FunctionCallSequence::get_indent(std::string& _str, File* _dfile, bool _is_html)
938
939
{
  int num = number_calls();
940
941
  for (int i = 0; i < num; ++i)
  {
942
    Enter* deb = debs_[i];
943
944
945
946
947
948
949
    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];
950
      sprintf_s(hovert, sizeof(hovert), "%s[%i]", func_name_.c_str(), deb->nmbr_);
951
      int col = 0xFFFFFF;
952
953
954
955
956
957
958
959
      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);
960
      if ((deb->outs_ > 0) && _dfile->link_to(_str, deb->id_, "enter", cstk, true))
961
962
963
964
965
966
      {
        _str.append("&lt;");
        _dfile->link_to(_str, deb->id_, "enter", cstk,  false);
      }
      else _str.append("&lt;");

967
      _str.append(deb->flnm_, 1);
968
969
970
971
972

      if (_dfile->link_to(_str, deb->id_, "exit", cstk, true))
      {
        _str.append("&gt;");
        _dfile->link_to(_str, deb->id_, "exit", cstk,  false);
973
        ++deb->lns_;
974
975
976
977
978
979
980
981
982
983
      }

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

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

984
bool CallStack::get_indent(std::string& _str, File* _dfile, const bool is_html)
985
986
987
988
989
990
991
992
993
994
{

  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();
995
996
  int i0 = 0;
  if (!is_html) ++i0; // Don't waste whitespace on first level indent if .txt
997
  for (int i = i0; i < num; ++i)
998
    calls_[i].get_indent(_str, _dfile,  is_html);
999
1000
1001
1002
1003
  if (is_html) _str.append(":&nbsp;</FONT>\n");
  return true;
}


1004
// =====================================
1005
//        Stream member funcs
1006
1007
1008
// =====================================


1009
1010
1011
Stream::Stream(const char* _flnm, const uint _flgs)
: dfile_(new File(_flnm, _flgs))
{}
1012

1013
Stream::~Stream()
1014
{
1015
  if (dfile_ != nullptr)
1016
  {
1017
1018
    dfile()->close();
    dfile()->clear();
1019
    // NB. disable DEB_out over this delete if delete contains DEB_out
1020
    delete dfile_;
1021
1022
1023
  }
}

1024
const std::string& Stream::string() const { return dfile()->string(); }
1025

1026
Stream& Stream::print(const int _i)
1027
{
1028
  dfile_->print(_i);
1029
1030
1031
  return *this;
};

1032
Stream& Stream::print(const double _d)
1033
{
1034
  dfile_->print(_d);
1035
1036
1037
  return *this;
};

1038
Stream& Stream::print(const char* _s, bool _fork)
1039
{
1040
  dfile_->print(_s, _fork);
1041
1042
1043
  return *this;
};

1044
Stream& Stream::print(const char _c)
1045
{
1046
1047
1048
1049
  dfile_->print(_c);
  return *this;
};

1050
Stream& Stream::print(const Command& _co)
1051
1052
{
  dfile_->print(_co);
1053
1054
1055
  return *this;
};

1056
Stream& operator<<(Stream& _ds, const int _i)
1057
1058
1059
{
  return _ds.print(_i);
}
1060
Stream& operator<<(Stream& _ds, const double _d)
1061
1062
1063
{
  return _ds.print(_d);
}
1064
Stream& operator<<(Stream& _ds, const char* const _s)
1065
1066
1067
{
  return _ds.print(_s);
}
1068
Stream& operator<<(Stream& _ds, const char _c)
1069
1070
1071
1072
{
  return _ds.print(_c);
}

1073
Stream& operator<<(Stream& _ds, const size_t _i)
1074
{
1075
  return _ds.print((int)_i);
1076
}
1077
Stream& operator<<(Stream& _ds, const unsigned int _i)
1078
{
1079
  return _ds.print((int)_i);
1080
}