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 "DebDefault.hh"
13
#include "Base/Code/CodeLink.hh"
14
#include "Base/Utils/ThrowError.hh"
15
#include "Base/Utils/Environment.hh"
16
#include "Base/Test/IChecksum.hh"
17
#include "Base/Test/ChecksumDebugEvent.hh"
18
19
20

#ifdef DEB_ON

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

#ifndef WIN32
  #define sprintf_s snprintf
#endif
37

38
39
40
namespace {

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

50
}
51

52
namespace Debug {
53
54

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

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

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

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

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

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

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

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

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

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

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

166
}; // endclass FunctionCallSequence
167

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

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

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

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

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

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

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

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

232
//////////////////////////////////////////////////////////////////////////
233
class File
234
235
{
public:
236
237
238
239
240
241
242
243
244
245
  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
  }
246

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

406
407
408
409
410
411
412
413
  void print(const size_t _i)
  {
    char buffer[128];
    sprintf_s(buffer, sizeof(buffer), "%lu", _i);
    print(buffer);
  }

  void print(const int _i)
414
415
416
417
418
  {
    char buffer[64];
    sprintf_s(buffer, sizeof(buffer), "%i", _i);
    print(buffer);
  }
419

420

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

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


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

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

466
467
468
469
      break;
    }
  }

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

497

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

514
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
515
516
517
518
519
520
521
522
523
524
525
526
  {
    if (is_html())
    {
      char buffer[1024];
      if (_open)  sprintf_s(buffer, sizeof(buffer), "<A name=\"%08X_%s\">", _id, _tag);
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }

527
  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
528
529
530
531
532
533
534
535
536
537
  {
    if (is_html())
    {
      char buffer[2048];
      if (_open)
      {
        // HTML title hover text is cropped to 64 char in Firefox but displays
        // OK in Chrome. We could use javascript to avoid this limit but HTML
        // is simpler.
        if (_hover.empty()) sprintf_s(buffer, sizeof(buffer),
538
539
540
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
541
542
543
544
545
546
547
548
549
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


550
551
552
  void header(std::string& str)
  {
    if (is_html())
553
554
555
556
557
558
559
560
561
    {
      str.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\">");
      str.append("\n<HTML><HEAD>");
      str.append("\n<TITLE>ReForm DEB_out");
      str.append("</TITLE>");
      // javascript lib loads go here
      // stylesheet loads go here
      // within HEAD javascript goes here
      str.append("\n</HEAD>");
562
      if (is_white_on_black())
563
      {
564
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
565
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
566
567
568
569
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
570
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
571
572
573
574
575
576
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
577
      if (!flnm_.empty())
578
      {
579
        str.append(flnm_);
580
581
        str.append(" opened ");
      }
582
      add_time(str);
583
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
584
585
      if (is_html()) str.append("<BR>");
      str.append("\n");
586
    }
587
   }
588
589
590
591
592
593

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
594
      std::string str("\n");
595
      if (!flnm_.empty()) str.append(flnm_);
596
      str.append(" Closed: ");
597
      add_time(str);
598
599
      str.append("\n");
      print(str.c_str());
600
601
602
    }

    if (is_html())
603
      print("\n</BODY></HTML>", false);
604
  }
605

606
607
608
609
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
610

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

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

649
          if (is_retained())
650
651
            output_.append(current_);

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

663
  // Use with extreme caution.
664
  const std::string& string() const { return current_; }
665

666
667
668
669
670
671
  void close()
  {
    footer();
    flush();
  }

672
  void set_filename(const char* _flnm)
673
  {
674
675
676
    flnm_ = _flnm != nullptr ? _flnm : "";
    if (is_html_filename(_flnm))
      flags_ = flags_ | Stream::HTML;
677
  }
678

679
  int permission(const char* const _flnm)
680
  {
681
682
    int lev = lev_;
    for (const auto& fltrs : level_selc_map_)
683
    {
684
685
      if (fltrs.second.select_file(_flnm) ||
          fltrs.second.select_function(call_stack_.call()->name()))
686
687
688
      {// continue this iteration until the maximum allowed level if found
        if (lev < fltrs.first) 
          lev = fltrs.first;
689
      }
690
    }
691
692
    return lev;
  }
693
694

  bool is_at_line_start()
695
  {
696
    return at_line_start_;
697
698
  }

699
700
  void read_debug_config()
  {
701
702
703
    const auto flnm = 
      System::Environment::variable("REFORM_DEB_CONFIG", "reform_deb.cfg");

704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
    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;
721
722
      //if (lev < 0 || lev > 15)
      //  continue;
723
724
725
726
727
728
729
730
731
732
733
734
735
      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);
    }
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
789
790
791
792
793
794
795
796
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_;
797
}; // endclass File
798

799
800
//////////////////////////////////////////////////////////////////////////

801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
namespace {

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

}//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)
{  
818
819
  Base::CodeLink code_link(_fnct, _file, _line);
  TEST_only(Test::Checksum::Debug::warning.record(_wrng, code_link));
820
  global_stream() << WARNING << ": " << _wrng << code_link << Base::Command::END_LF;
821
822
823
824
825
}

void error(const std::string& _err, const char* const _fnct, 
  const char* const _file, const int _line)
{
826
827
  Base::CodeLink code_link(_fnct, _file, _line);
  TEST_only(Test::Checksum::Debug::error.record(_err, code_link));
828
  global_stream() << ERROR << ": " << _err << code_link << Base::Command::END_ERR;
829
830
}

831
#undef TRIGGER_POINT
832
833
834

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

835
836
837
838
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!
839
  global_stream().dfile()->call_stack().add(_fnct, this);
840

841
842
843
  nmbr_ = _nmbr++; 

  if (_lvl == INVALID_LEVEL)
844
    _lvl = global_stream().dfile()->permission(flnm_);
845
  lvl_ = _lvl;
846

847
  static int id_cnt = 0;
848
  id_ = ++id_cnt;
849
850
}

851
Enter::~Enter()
852
{
853
  File* impl = global_stream().dfile();
854
  impl->call_stack().pop();
855

856
  std::string str;
857
  if (((outs_ > 0) || (lns_ > 0)) && impl->anchor(str, id_, "exit", true))
858
  {
859
    impl->anchor(str, id_, "exit", false);
860
861
    impl->print_direct(str);
  }
862
863
}

864
Stream& Enter::stream()
865
{
866
  Stream& ds = global_stream();
867
  File* impl = ds.dfile();
868

869
870
871
872
873
874
875
876
877
878
879
880
881
  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)
882
  {
883
884
885
886
887
    // 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)
888
    {
889
890
      str.append("<FONT SIZE=2><u>");
      impl->anchor(str, id_, "enter", true);
891
    }
892
    else
893
    {
894
895
      // .txt call stack lead in
      str.append("****>");
896
    }
897
898
899
900
901
902

    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();
903
  }
904
  ++outs_;
905
906
907
908

  return ds;
}

909
void FunctionCallSequence::get_indent(std::string& _str, File* _dfile, bool _is_html)
910
911
{
  int num = number_calls();
912
913
  for (int i = 0; i < num; ++i)
  {
914
    Enter* deb = debs_[i];
915
916
917
918
919
920
921
    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];
922
      sprintf_s(hovert, sizeof(hovert), "%s[%i]", func_name_.c_str(), deb->nmbr_);
923
      int col = 0xFFFFFF;
924
925
926
927
928
929
930
931
      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);
932
      if ((deb->outs_ > 0) && _dfile->link_to(_str, deb->id_, "enter", cstk, true))
933
934
935
936
937
938
      {
        _str.append("&lt;");
        _dfile->link_to(_str, deb->id_, "enter", cstk,  false);
      }
      else _str.append("&lt;");

939
      _str.append(deb->flnm_, 1);
940
941
942
943
944

      if (_dfile->link_to(_str, deb->id_, "exit", cstk, true))
      {
        _str.append("&gt;");
        _dfile->link_to(_str, deb->id_, "exit", cstk,  false);
945
        ++deb->lns_;
946
947
948
949
950
951
952
953
954
955
      }

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

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

956
bool CallStack::get_indent(std::string& _str, File* _dfile, const bool is_html)
957
958
959
960
961
962
963
964
965
966
{

  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();
967
968
  int i0 = 0;
  if (!is_html) ++i0; // Don't waste whitespace on first level indent if .txt
969
  for (int i = i0; i < num; ++i)
970
    calls_[i].get_indent(_str, _dfile,  is_html);
971
972
973
974
975
  if (is_html) _str.append(":&nbsp;</FONT>\n");
  return true;
}


976
// =====================================
977
//        Stream member funcs
978
979
980
// =====================================


981
982
983
Stream::Stream(const char* _flnm, const uint _flgs)
: dfile_(new File(_flnm, _flgs))
{}
984

985
Stream::~Stream()
986
{
987
  if (dfile_ != nullptr)
988
  {
989
990
    dfile()->close();
    dfile()->clear();
991
    // NB. disable DEB_out over this delete if delete contains DEB_out
992
    delete dfile_;
993
994
995
  }
}

996
const std::string& Stream::string() const { return dfile()->string(); }
997

998
999
1000
1001
1002
1003
1004
Base::IOutputStream& Stream::print(const int _i)
{
  dfile_->print(_i);
  return *this;
};

Base::IOutputStream& Stream::print(const size_t _i)
1005
{
1006
  dfile_->print(_i);
1007
1008
1009
  return *this;
};

1010
Base::IOutputStream& Stream::print(const double _d)
1011
{
1012
  dfile_->print(_d);
1013
1014
1015
  return *this;
};

1016
Base::IOutputStream& Stream::print(const char* _s)
1017
{
1018
  dfile_->print(_s);
1019
1020
1021
  return *this;
};

1022
Base::IOutputStream& Stream::print(const char _c)
1023
{
1024
1025
1026
1027
  dfile_->print(_c);
  return *this;
};

1028
Base::IOutputStream& Stream::print(const Base::Command& _co)
1029
1030
{
  dfile_->print(_co);
1031
1032
1033
  return *this;
};

1034
// ==================================================
1035
//  Controller member funcs (currently all static)
1036
1037
// ==================================================

1038
void Controller::set_double_format(const char* _fmt)
1039
{
1040
  global_stream().dfile()->set_double_format(_fmt);
1041
}
1042

1043
const char* Controller::double_format()
1044
{
1045
  return global_stream().dfile()->double_format