/* KLayout Layout Viewer Copyright (C) 2006-2017 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "utTestConsole.h" #include "utHead.h" #include "rba.h" #include "pya.h" #include #if !defined(_WIN32) # include # include #endif #if defined(_WIN32) # include #endif namespace ut { // ------------------------------------------------ // CaptureChannel implementation CaptureChannel::CaptureChannel () { tl::info.add (this, false); tl::error.add (this, false); tl::warn.add (this, false); } void CaptureChannel::puts (const char *s) { m_text << s; } void CaptureChannel::endl () { m_text << "\n"; } void CaptureChannel::end () { // .. nothing yet .. } void CaptureChannel::begin () { // .. nothing yet .. } // ------------------------------------------------ // tl::Channel implementations for redirecting the log output class InfoChannel : public tl::Channel { public: InfoChannel (int verbosity) : m_verbosity (verbosity) { // .. nothing yet .. } protected: virtual void puts (const char *s) { if (tl::verbosity () >= m_verbosity) { TestConsole::instance ()->basic_write (s); } } virtual void endl () { if (tl::verbosity () >= m_verbosity) { TestConsole::instance ()->basic_write ("\n"); } } virtual void end () { TestConsole::instance ()->flush (); } virtual void begin () { // .. nothing yet .. } private: int m_verbosity; }; class WarningChannel : public tl::Channel { public: WarningChannel () { // .. nothing yet .. } protected: virtual void puts (const char *s) { TestConsole::instance ()->basic_write (s); } virtual void endl () { TestConsole::instance ()->basic_write ("\n"); } virtual void end () { TestConsole::instance ()->end (); TestConsole::instance ()->flush (); } virtual void begin () { TestConsole::instance ()->begin_warn (); } }; class ErrorChannel : public tl::Channel { public: ErrorChannel () { // .. nothing yet .. } protected: virtual void puts (const char *s) { TestConsole::instance ()->basic_write (s); } virtual void endl () { TestConsole::instance ()->basic_write ("\n"); } virtual void end () { TestConsole::instance ()->end (); TestConsole::instance ()->flush (); } virtual void begin () { TestConsole::instance ()->begin_error (); } }; class CtrlChannel : public tl::Channel { public: CtrlChannel (bool with_xml) : m_with_xml (with_xml) { // .. nothing yet .. } protected: virtual void puts (const char *s) { if (m_with_xml == TestConsole::instance ()->xml_format ()) { TestConsole::instance ()->raw_write (s); } } virtual void endl () { if (m_with_xml == TestConsole::instance ()->xml_format ()) { TestConsole::instance ()->raw_write ("\n"); } } virtual void end () { if (m_with_xml == TestConsole::instance ()->xml_format ()) { TestConsole::instance ()->end (); TestConsole::instance ()->flush (); } } virtual void begin () { if (m_with_xml == TestConsole::instance ()->xml_format ()) { TestConsole::instance ()->begin_info (); } } private: bool m_with_xml; }; // ------------------------------------------------ // TestConsole implementation const char *ANSI_RED = "\033[31;1m"; const char *ANSI_BLUE = "\033[34m"; const char *ANSI_GREEN = "\033[32m"; const char *ANSI_RESET = "\033[0m"; TestConsole::TestConsole (FILE *file, bool xml_format) : m_file (file), m_xml_format (xml_format), m_col (0), m_max_col (250), m_columns (50), m_rows (0), m_is_tty (false) { ms_instance = this; m_indent = 4; m_is_tty = isatty (fileno (file)) && ! xml_format; #if !defined(_WIN32) if (m_is_tty) { struct winsize ws; ioctl (fileno (stdout), TIOCGWINSZ, &ws); m_columns = std::max (0, (int) ws.ws_col); m_rows = std::max (0, (int) ws.ws_row); } #endif redirect (); } TestConsole::~TestConsole () { restore (); if (ms_instance == this) { ms_instance = 0; } } void TestConsole::write_str (const char *text, output_stream os) { if (os == OS_stderr) { begin_error (); basic_write (text); end (); } else { basic_write (text); } } void TestConsole::raw_write (const char *text) { fputs (text, m_file); } void TestConsole::flush () { fflush (m_file); } void TestConsole::begin_error () { if (m_is_tty) { fputs (ANSI_RED, m_file); } } void TestConsole::begin_info () { if (m_is_tty) { fputs (ANSI_GREEN, m_file); } } void TestConsole::begin_warn () { if (m_is_tty) { fputs (ANSI_BLUE, m_file); } } void TestConsole::end () { if (m_is_tty) { fputs (ANSI_RESET, m_file); } } void TestConsole::basic_write (const char *s) { if (m_xml_format) { for (const char *cp = s; *cp; ++cp) { if (*cp == '&') { fputs ("&", m_file); } else if (*cp == '<') { fputs ("<", m_file); } else if (*cp == '>') { fputs (">", m_file); } else { fputc (*cp, m_file); } } } else { // line length limitation - this assumes we are always printing to the same terminal // or we don't mix stderr/stdout. const char *cp; for (cp = s; *cp; ++cp) { if (*cp == '\n' || *cp == '\r') { m_col = 0; fputc (*cp, m_file); } else { if (m_col == 0) { for (int i = 0; i < m_indent; ++i) { fputc (' ', m_file); } m_col = m_indent; } if (m_col > m_max_col) { // ignore char } else if (m_col == m_max_col) { fputs (" ...", m_file); ++m_col; } else if (*cp == '\033') { // skip ANSI escape sequences (no increment of s_col) const char *cpend = cp + 1; if (*cpend == '[') { ++cpend; while (*cpend && *cpend != 'm') { ++cpend; } if (*cpend) { ++cpend; } } while (cp != cpend) { fputc (*cp++, m_file); } --cp; } else { fputc (*cp, m_file); ++m_col; } } } } } void TestConsole::redirect () { // redirect the log channels tl::warn.clear (); tl::warn.add (new ut::WarningChannel (), true); tl::info.clear (); tl::info.add (new ut::InfoChannel (0), true); tl::log.clear (); tl::log.add (new ut::InfoChannel (10), true); tl::error.clear (); tl::error.add (new ut::ErrorChannel (), true); ut::ruby_interpreter ()->push_console (this); ut::python_interpreter ()->push_console (this); } void TestConsole::restore () { // TODO: we should basically restore the original channels tl::warn.clear (); tl::info.clear (); tl::log.clear (); tl::error.clear (); ut::ruby_interpreter ()->remove_console (this); ut::python_interpreter ()->remove_console (this); } TestConsole *TestConsole::ms_instance = 0; tl::LogTee noctrl (new CtrlChannel (false), true); tl::LogTee ctrl (new CtrlChannel (true), true); }