DebFile.cc 10.2 KB
Newer Older
1 2 3 4 5 6
// (C) Copyright 2016 by Autodesk, Inc.

#ifdef DEB_ON
#include "DebFile.hh"
#include "DebCallStack.hh"
#include "DebDefault.hh"
7

8
#include "Base/Utils/Environment.hh"
9 10 11 12 13 14 15 16 17 18 19 20 21

#include <string>
#include <fstream>
#include <time.h>
#include <vector>
#include <iostream>
#include <map>
#include <memory>
#include <list>
#include <map>
#include <sstream>
#include <cstring>

Max Lyon's avatar
Max Lyon committed
22
#if !defined(WIN32) && !defined(_WIN32)
23 24 25 26 27 28 29 30
  #define sprintf_s snprintf
#endif

namespace Debug {
namespace {  
// TODO: make this use std::string; check for html extension; case insensitive
bool is_html_filename(const char* const str)
{
Max Lyon's avatar
Max Lyon committed
31
  if (str == NULL) return false;
32
  const char* dot = strrchr(str, '.');
Max Lyon's avatar
Max Lyon committed
33
  if (dot == NULL) return false;
34 35 36 37 38 39 40 41
  ++dot;
  return (!strncmp(dot, "htm", 3)) || (!strncmp(dot, "HTM", 3)) ;
}
}//namespace

class File::Impl
{
public:
42
  Impl(const char* const _flnm, const uint _flags) 
43
    : flags_(_flags), num_flush_(0), line_strt_(false) 
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
  {
    set_filename(_flnm);
  }

  ~Impl()
  {
    close();
    clear();
  }

  bool is_kept_open() const
  {
    return 0 != (flags_ & KEEP_OPEN);
  }

  bool is_html() const
  {
    return 0 != (flags_ & HTML);
  }

  bool is_retained() const
  {
    return 0 != (flags_ & RETAIN);
  }

  bool is_appended() const
  {
    return 0 != (flags_ & APPEND);
  }
  
  // Only applies to HTML DEB_out
  bool is_white_on_black() const
  {
    return true;
  }

  bool file_is_open() const
  {
    return file_stream_.is_open();
  }

  int priority() const
  {
    return priority_;
  }

90 91 92
  const char* filename() const 
  { 
    return flnm_.empty() ? NULL : flnm_.c_str(); 
93 94 95 96
  }

  void clear()
  {
97
    bffr_.clear();
98 99 100 101
    output_.clear();
    flnm_.clear();
  }

102
  void print(const char _c, const bool _cnsl = true)
103
  {
104
    if (line_strt_)
105
    {
106 107
      line_strt_ = false;
      print(' ', false); // indents never go onto the console!
108
    }
109
    bffr_.append(&_c, 1);
110
    if (_cnsl && console())
111
      std::cerr << _c; // print on the console
112
    if (_c == '\n')
113
    {
114
      std::cerr << std::flush;
115
      line_strt_ = true;
116
    }
117 118
  }

119 120 121
  void line_break(const bool _cnsl = true) { print('\n', _cnsl); }

  void print(const std::string& _s, const bool _cnsl = true)
122
  {
123 124
    for (size_t i = 0, n = _s.size(); i < n; ++i)
      print(_s[i], _cnsl);
125 126
  }

127
  void print(const char* const _s, const bool _cnsl = true)
128
  {
Max Lyon's avatar
Max Lyon committed
129
    if (_s == NULL)
130 131 132
      return;
    for (int i = 0, c = _s[0]; c != '\0'; c = _s[++i])
      print((char)c, _cnsl);
133 134 135 136 137
  }

  void print(const size_t _i)
  {
    char buffer[128];
138 139 140
#if defined(_MSC_VER) && _MSC_VER < 1900 // MSVC versions older than VC2015
    sprintf_s(buffer, sizeof(buffer), "%Iu", _i);
#else // MSVC 2015 and everything else
141
    sprintf_s(buffer, sizeof(buffer), "%zu", _i);
142
#endif
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
    print(buffer);
  }

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

  const char* double_format() const
  {
    if (double_format_.empty())
      return "%.17g";
    return double_format_.c_str();
  }

  void set_double_format(const char* const str)
  {
Max Lyon's avatar
Max Lyon committed
162
    if (str == NULL)
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
      double_format_.clear();
    else
      double_format_ = str;
  }

  void print(double _d)
  {
    char buffer[64];
    sprintf_s(buffer, sizeof(buffer), double_format(), _d);
    print(buffer);
  }

  void print(const Base::Command& _co)
  {
    switch (_co.cmd)
    {
    case Base::Command::END :
      break;
    case Base::Command::END_ERR :
    case Base::Command::END_LF :
      line_break(); 
      break;
    }
  }

  // Append current asctime to given string
189
  void add_time(std::string& str) { str += System::Environment::time(); }
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285

#if 1
  bool hover(std::string& _str, const std::string& _hover, const bool _open)
  {
    if (is_html())
    {
      char buffer[1024];
      if (_open)  sprintf_s(buffer, sizeof(buffer),
                              "<span title=\"%s\">", _hover.c_str());
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
  {
    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;
  }

  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
  {
    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),
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


  void header(std::string& str)
  {
    if (is_html())
    {
      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>");
      if (is_white_on_black())
      {
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
      if (!flnm_.empty())
      {
        str.append(flnm_);
        str.append(" opened ");
      }
      add_time(str);
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
      if (is_html()) str.append("<BR>");
      str.append("\n");
    }
   }

  void footer()
  {
286 287 288 289 290 291 292
    std::string str("\n");
    if (!flnm_.empty()) 
      str.append(flnm_);
    str.append(" Closed: ");
    add_time(str);
    str.append("\n");
    print(str.c_str());
293 294
  }

295
  bool is_first_flush() { return num_flush_ == 0; }
296

297
  bool flush()
298
  {
299
    if (bffr_.empty() || !logfile())
300 301 302
      return true;

    const char* const flnm = filename();
Max Lyon's avatar
Max Lyon committed
303
    if (flnm == NULL && !file_is_open())
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
      return false;

    if (!file_is_open())
    {
      file_stream_.open(flnm, std::fstream::out | 
        ((is_appended() && !is_first_flush()) ? 
          std::fstream::app : std::fstream::trunc));
    }

    if (!file_is_open()) // failed opening the file
      return false;

    std::string hdr;
    if (!is_appended())
    {// Re-output entire file
      header(hdr);
      output_.append(hdr);
      file_stream_ << output_;
    }
    else
324
    {
325
      if (is_first_flush())
326
      {
327 328 329 330
        header(hdr);
        if (is_retained())
          output_.append(hdr);
        file_stream_ << hdr;
331
      }
332 333 334 335
      ++num_flush_;
    }

    file_stream_ << bffr_;
336

337 338 339 340 341 342 343 344 345
    if (is_retained())
      output_.append(bffr_);

    bffr_.clear();
    if (!is_kept_open())
      file_stream_.close();

    return true;
  }
346 347 348 349 350 351 352

  void close()
  {
    footer();
    flush();
  }

353
  void set_filename(const char* const _flnm)
354
  {
Max Lyon's avatar
Max Lyon committed
355
    flnm_ = _flnm != NULL ? _flnm : "";
356 357 358 359
    if (is_html_filename(_flnm))
      flags_ = flags_ | HTML;
  }

360
  void enter()
361
  {
362
    const bool entr_cnsl = false; // do we print enters on the console?
363
    // First DEB_out in this function so output call-stack, and flush.
364 365 366
    if (!line_strt_)
      line_break(entr_cnsl); // make sure we start on a new line with this
    
367
    std::string str;
368
    str.append("*>"); // .txt call stack lead in
369
    CallStack::query().append(str);
370
    bffr_.append(str);
371

372
    line_break(entr_cnsl); // make sure we start on a new line with this
373 374 375
    flush();
  }

376 377 378 379 380 381
  void set_console(const bool _on = true) { set_flag<CONSOLE>(_on); }
  bool console() const { return flag<CONSOLE>(); }

  void set_logfile(bool _on) { set_flag<LOGFILE>(_on); }
  bool logfile() const { return flag<LOGFILE>(); }

382 383 384 385
private:
  uint flags_;
  int num_flush_;
  int priority_; // Last permission granted
386
  bool line_strt_; // are we at the start of th line?
387

388
  std::string bffr_;
389 390 391 392 393
  std::string output_;
  std::string flnm_;
  std::fstream file_stream_;

  std::string double_format_;
394 395 396 397 398 399 400 401 402 403 404 405

private:

  template <Flags _flag>
  void set_flag(const bool _on)
  {
    flags_ = _on ? flags_ | _flag : flags_ & ~_flag;
  }

  template <Flags _flag>
  bool flag() const { return (flags_ & _flag) == _flag; }

406 407 408 409 410 411 412 413 414 415 416
};

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

File& File::modify()
{
  // TODO: Thread-local storage, each (per thread) file in a separate  folder
  static File glbl_file(Default::LOG_FILENAME);
  return glbl_file;
}

417 418
const File& File::query() { return modify(); }

419 420 421 422 423 424 425 426 427
File::File(const char* const _flnm, const uint _flags)
  : impl_(new Impl(_flnm, _flags))
{}

File::~File()
{
  delete impl_;
}

428
void File::enter(const int /*_id*/) { impl_->enter(/*_id*/); }
429 430 431 432 433 434 435 436 437 438 439 440 441 442

void File::print(const char _c) { impl_->print(_c); }
void File::print(const char* _s) { impl_->print(_s); }
void File::print(const size_t _i) { impl_->print(_i); }
void File::print(const int _i) { impl_->print(_i); }
void File::print(double _d) { impl_->print(_d); }
void File::print(const Base::Command& _co) { impl_->print(_co); }

const char* File::double_format() const { return impl_->double_format(); }
void File::set_double_format(const char* const str) 
{ 
  impl_->set_double_format(str); 
}

443 444
void File::set_console(const bool _on) { impl_->set_console(_on); }
bool File::console() const { return impl_->console(); }
445

446 447
void File::set_logfile(bool _on) { impl_->set_logfile(_on); }
bool File::logfile() const { return impl_->logfile(); }
448

Max Lyon's avatar
Max Lyon committed
449 450 451 452
void File::set_filename(const char* const _flnm) { impl_->set_filename(_flnm); }

void File::close() { impl_->close(); }

453 454 455
}//namespace Debug

#endif // DEB_ON