/*************************************************************************** * (c) Jürgen Riegel (juergen.riegel@web.de) 2002 * * * * This file is part of the FreeCAD CAx development system. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License (LGPL) * * as published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * for detail see the LICENCE text file. * * * * FreeCAD 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 Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with FreeCAD; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * * * * Juergen Riegel 2002 * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ # include # include # ifdef FC_OS_WIN32 # include # include # endif # include "fcntl.h" #endif #include "Console.h" #include "Exception.h" #include "PyObjectBase.h" using namespace Base; char format[4024]; // global buffer const unsigned int format_len = 4024; //************************************************************************** // Construction destruction ConsoleSingleton::ConsoleSingleton(void) :_bVerbose(false) { } ConsoleSingleton::~ConsoleSingleton() { for(std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();Iter++) delete (*Iter); } //************************************************************************** // methods /** * sets the console in a special mode */ void ConsoleSingleton::SetMode(ConsoleMode m) { if(m && Verbose) _bVerbose = true; } /** * unsets the console from a special mode */ void ConsoleSingleton::UnsetMode(ConsoleMode m) { if(m && Verbose) _bVerbose = false; } /** * \a type can be OR'ed with any of the FreeCAD_ConsoleMsgType flags to enable -- if \a b is true -- * or to disable -- if \a b is false -- a console observer with name \a sObs. * The return value is an OR'ed value of all message types that have changed their state. For example * @code * // switch off warnings and error messages * ConsoleMsgFlags ret = Base::Console().SetEnabledMsgType("myObs", * ConsoleMsgType::MsgType_Wrn|ConsoleMsgType::MsgType_Err, false); * // do something without notifying observer myObs * ... * // restore the former configuration again * Base::Console().SetEnabledMsgType("myObs", ret, true); * @endcode * switches off warnings and error messages and restore the state before the modification. * If the observer \a sObs doesn't exist then nothing happens. */ ConsoleMsgFlags ConsoleSingleton::SetEnabledMsgType(const char* sObs, ConsoleMsgFlags type, bool b) { ConsoleObserver* pObs = Get(sObs); if ( pObs ){ ConsoleMsgFlags flags=0; if ( type&MsgType_Err ){ if ( pObs->bErr != b ) flags |= MsgType_Err; pObs->bErr = b; } if ( type&MsgType_Wrn ){ if ( pObs->bWrn != b ) flags |= MsgType_Wrn; pObs->bWrn = b; } if ( type&MsgType_Txt ){ if ( pObs->bMsg != b ) flags |= MsgType_Txt; pObs->bMsg = b; } if ( type&MsgType_Log ){ if ( pObs->bLog != b ) flags |= MsgType_Log; pObs->bLog = b; } return flags; } else { return 0; } } /** Prints a Message * This method issues a Message. * Messages are used show some non vital information. That means in the * case FreeCAD running with GUI a Message in the status Bar apear. In console * mode a message comes out. * \par * You can use a printf like interface like: * \code * Console().Message("Doing somthing important %d times\n",i); * \endcode * @see Warning * @see Error * @see Log */ void ConsoleSingleton::Message( const char *pMsg, ... ) { va_list namelessVars; va_start(namelessVars, pMsg); // Get the "..." vars vsnprintf(format, format_len, pMsg, namelessVars); va_end(namelessVars); NotifyMessage(format); } /** Prints a Message * This method issues a Warning. * Messages are used to get the users attantion. That means in the * case FreeCAD running with GUI a Message Box is poping up. In console * mode a colored message comes out! So dont use careless. For information * purpose the Log or Message method is more aprobiated. * \par * You can use a printf like interface like: * \code * Console().Warning("Some defects in %s, loading anyway\n",str); * \endcode * @see Message * @see Error * @see Log */ void ConsoleSingleton::Warning( const char *pMsg, ... ) { va_list namelessVars; va_start(namelessVars, pMsg); // Get the "..." vars vsnprintf(format, format_len, pMsg, namelessVars); va_end(namelessVars); NotifyWarning(format); } /** Prints a Message * This method issues an Error which makes some execution imposible. * Errors are used to get the users attantion. That means in the * case FreeCAD running with GUI a Error Message Box is poping up. In console * mode a colored message comes out! So dont use this careless. For information * purpose the Log or Message method is more aprobiated. * \par * You can use a printf like interface like: * \code * Console().Error("Somthing realy bad in %s happend\n",str); * \endcode * @see Message * @see Warning * @see Log */ void ConsoleSingleton::Error( const char *pMsg, ... ) { va_list namelessVars; va_start(namelessVars, pMsg); // Get the "..." vars vsnprintf(format, format_len, pMsg, namelessVars); va_end(namelessVars); NotifyError(format); } /** Prints a Message * this method is more for devlopment and tracking purpos. * It can be used to track execution of algorithems and functions * and put it in files. The normal user dont need to see it, its more * for developers and experinced users. So in normal user modes the * logging is switched of. * \par * You can use a printf like interface like: * \code * Console().Log("Exectue part %d in algorithem %s\n",i,str); * \endcode * @see Message * @see Warning * @see Error */ void ConsoleSingleton::Log( const char *pMsg, ... ) { if (!_bVerbose) { va_list namelessVars; va_start(namelessVars, pMsg); // Get the "..." vars vsnprintf(format, format_len, pMsg, namelessVars); va_end(namelessVars); NotifyLog(format); } } /** Delivers the time/date * this method give you a string with the actual time/date. You can * use that for Log() calls to make time stamps. * @return Const string with the date/time */ const char* ConsoleSingleton::Time(void) { struct tm *newtime; time_t aclock; time( &aclock ); // Get time in seconds newtime = localtime( &aclock ); // Convert time to struct tm form char* st = asctime( newtime ); st[24] = 0; return st; } //************************************************************************** // Observer stuff /** Attaches an Observer to Console * Use this method to attach a ConsoleObserver derived class to * the Console. After the observer is attached all messages will also * forwardet to it. * @see ConsoleObserver */ void ConsoleSingleton::AttachObserver(ConsoleObserver *pcObserver) { // double insert !! assert(_aclObservers.find(pcObserver) == _aclObservers.end() ); _aclObservers.insert(pcObserver); } /** Detaches an Observer from Console * Use this method to detach a ConsoleObserver derived class. * After detaching you can destruct the Observer or reinsert it later. * @see ConsoleObserver */ void ConsoleSingleton::DetachObserver(ConsoleObserver *pcObserver) { _aclObservers.erase(pcObserver); } void ConsoleSingleton::NotifyMessage(const char *sMsg) { for(std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();Iter++) if((*Iter)->bMsg) (*Iter)->Message(sMsg); // send string to the listener } void ConsoleSingleton::NotifyWarning(const char *sMsg) { for(std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();Iter++) if((*Iter)->bWrn) (*Iter)->Warning(sMsg); // send string to the listener } void ConsoleSingleton::NotifyError(const char *sMsg) { for(std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();Iter++) if((*Iter)->bErr) (*Iter)->Error(sMsg); // send string to the listener } void ConsoleSingleton::NotifyLog(const char *sMsg) { for(std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();Iter++) if((*Iter)->bLog) (*Iter)->Log(sMsg); // send string to the listener } ConsoleObserver *ConsoleSingleton::Get(const char *Name) { const char* OName; for(std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();Iter++) { OName = (*Iter)->Name(); // get the name if(OName && strcmp(OName,Name) == 0) return *Iter; } return 0; } //************************************************************************** // Singleton stuff ConsoleSingleton * ConsoleSingleton::_pcSingleton = 0; void ConsoleSingleton::Destruct(void) { // not initialized or double destructed! assert(_pcSingleton); delete _pcSingleton; _pcSingleton=0; } ConsoleSingleton & ConsoleSingleton::Instance(void) { // not initialized? if(!_pcSingleton) { _pcSingleton = new ConsoleSingleton(); } return *_pcSingleton; } //************************************************************************** // Python stuff // ConsoleSingleton Methods // Methods structure PyMethodDef ConsoleSingleton::Methods[] = { {"PrintMessage", (PyCFunction) ConsoleSingleton::sPyMessage, 1, "PrintMessage(string) -- Print a message to the output"}, {"PrintLog", (PyCFunction) ConsoleSingleton::sPyLog, 1, "PrintLog(string) -- Print a log message to the output"}, {"PrintError" , (PyCFunction) ConsoleSingleton::sPyError, 1, "PrintError(string) -- Print an error message to the output"}, {"PrintWarning", (PyCFunction) ConsoleSingleton::sPyWarning, 1, "PrintWarning -- Print a warning to the output"}, {"SetStatus", (PyCFunction) ConsoleSingleton::sPySetStatus, 1, "Set the status for either Log, Msg, Wrn or Error for an observer"}, {"GetStatus", (PyCFunction) ConsoleSingleton::sPyGetStatus, 1, "Get the status for either Log, Msg, Wrn or Error for an observer"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; PyObject *ConsoleSingleton::sPyMessage(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) { PyObject *output; if (!PyArg_ParseTuple(args, "O", &output)) return NULL; const char* string=0; PyObject* unicode=0; if (PyUnicode_Check(output)) { unicode = PyUnicode_AsEncodedObject(output, "utf-8", "strict"); if (unicode) string = PyString_AsString(unicode); } else if (PyString_Check(output)) { string = PyString_AsString(output); } else { unicode = PyObject_Str(output); if (unicode) string = PyString_AsString(unicode); } PY_TRY { if (string) Instance().Message("%s",string); // process message } PY_CATCH; Py_XDECREF(unicode); Py_INCREF(Py_None); return Py_None; } PyObject *ConsoleSingleton::sPyWarning(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) { PyObject *output; if (!PyArg_ParseTuple(args, "O", &output)) return NULL; const char* string=0; PyObject* unicode=0; if (PyUnicode_Check(output)) { unicode = PyUnicode_AsEncodedObject(output, "utf-8", "strict"); if (unicode) string = PyString_AsString(unicode); } else if (PyString_Check(output)) { string = PyString_AsString(output); } else { unicode = PyObject_Str(output); if (unicode) string = PyString_AsString(unicode); } PY_TRY { if (string) Instance().Warning("%s",string); // process message } PY_CATCH; Py_XDECREF(unicode); Py_INCREF(Py_None); return Py_None; } PyObject *ConsoleSingleton::sPyError(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) { PyObject *output; if (!PyArg_ParseTuple(args, "O", &output)) return NULL; const char* string=0; PyObject* unicode=0; if (PyUnicode_Check(output)) { unicode = PyUnicode_AsEncodedObject(output, "utf-8", "strict"); if (unicode) string = PyString_AsString(unicode); } else if (PyString_Check(output)) { string = PyString_AsString(output); } else { unicode = PyObject_Str(output); if (unicode) string = PyString_AsString(unicode); } PY_TRY { if (string) Instance().Error("%s",string); // process message } PY_CATCH; Py_XDECREF(unicode); Py_INCREF(Py_None); return Py_None; } PyObject *ConsoleSingleton::sPyLog(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) { PyObject *output; if (!PyArg_ParseTuple(args, "O", &output)) return NULL; const char* string=0; PyObject* unicode=0; if (PyUnicode_Check(output)) { unicode = PyUnicode_AsEncodedObject(output, "utf-8", "strict"); if (unicode) string = PyString_AsString(unicode); } else if (PyString_Check(output)) { string = PyString_AsString(output); } else { unicode = PyObject_Str(output); if (unicode) string = PyString_AsString(unicode); } PY_TRY { if (string) Instance().Log("%s",string); // process message } PY_CATCH; Py_XDECREF(unicode); Py_INCREF(Py_None); return Py_None; } PyObject *ConsoleSingleton::sPyGetStatus(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) { char *pstr1; char *pstr2; if (!PyArg_ParseTuple(args, "ss", &pstr1, &pstr2)) // convert args: Python->C return NULL; // NULL triggers exception PY_TRY{ bool b=false; ConsoleObserver *pObs = Instance().Get(pstr1); if(!pObs) { Py_INCREF(Py_None); return Py_None; } if(strcmp(pstr2,"Log") == 0) b = pObs->bLog; else if(strcmp(pstr2,"Wrn") == 0) b = pObs->bWrn; else if(strcmp(pstr2,"Msg") == 0) b = pObs->bMsg; else if(strcmp(pstr2,"Err") == 0) b = pObs->bErr; return Py_BuildValue("i",b?1:0); }PY_CATCH; } PyObject *ConsoleSingleton::sPySetStatus(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/) { char *pstr1; char *pstr2; int Bool; if (!PyArg_ParseTuple(args, "ssi", &pstr1, &pstr2,&Bool)) // convert args: Python->C return NULL; // NULL triggers exception PY_TRY{ ConsoleObserver *pObs = Instance().Get(pstr1); if(pObs) { if(strcmp(pstr2,"Log") == 0) pObs->bLog = (Bool==0)?false:true; else if(strcmp(pstr2,"Wrn") == 0) pObs->bWrn = (Bool==0)?false:true; else if(strcmp(pstr2,"Msg") == 0) pObs->bMsg = (Bool==0)?false:true; else if(strcmp(pstr2,"Err") == 0) pObs->bErr = (Bool==0)?false:true; else Py_Error(PyExc_Exception,"Unknown Message Type (use Log,Err,Msg or Wrn)"); Py_INCREF(Py_None); return Py_None; } else { Py_Error(PyExc_Exception,"Unknown Console Type"); } } PY_CATCH; } //========================================================================= // some special observers ConsoleObserverFile::ConsoleObserverFile(const char *sFileName) : cFileStream(Base::FileInfo(sFileName)) // can be in UTF8 { if (!cFileStream.is_open()) Console().Warning("Cannot open log file '%s'.\n", sFileName); // mark the file as a UTF-8 encoded file unsigned char bom[3] = {0xef, 0xbb, 0xbf}; cFileStream.write((const char*)bom,3*sizeof(char)); } ConsoleObserverFile::~ConsoleObserverFile() { cFileStream.close(); } void ConsoleObserverFile::Warning(const char *sWarn) { cFileStream << "Wrn: " << sWarn; cFileStream.flush(); } void ConsoleObserverFile::Message(const char *sMsg) { cFileStream << "Msg: " << sMsg; cFileStream.flush(); } void ConsoleObserverFile::Error (const char *sErr) { cFileStream << "Err: " << sErr; cFileStream.flush(); } void ConsoleObserverFile::Log (const char *sLog) { cFileStream << "Log: " << sLog; cFileStream.flush(); } ConsoleObserverStd::ConsoleObserverStd() { bLog = false; } ConsoleObserverStd::~ConsoleObserverStd() { } void ConsoleObserverStd::Message(const char *sMsg) { printf("%s",sMsg); } void ConsoleObserverStd::Warning(const char *sWarn) { # if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN| FOREGROUND_BLUE); # elif defined(FC_OS_LINUX) printf("\033[1;33m"); # endif printf("%s",sWarn); # if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); # elif defined(FC_OS_LINUX) printf("\033[0m"); # endif } void ConsoleObserverStd::Error (const char *sErr) { # if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_INTENSITY ); # elif defined(FC_OS_LINUX) printf("\033[1;31m"); # endif printf("%s",sErr); # if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); # elif defined(FC_OS_LINUX) printf("\033[0m"); # endif } void ConsoleObserverStd::Log (const char *sErr) { # if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED |FOREGROUND_GREEN); # elif defined(FC_OS_LINUX) printf("\033[1;36m"); # endif printf("%s",sErr); # if defined(FC_OS_WIN32) ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); # elif defined(FC_OS_LINUX) printf("\033[0m"); # endif } RedirectStdOutput::RedirectStdOutput() { buffer.reserve(80); } int RedirectStdOutput::overflow(int c) { if (c != EOF) buffer.push_back((char)c); return c; } int RedirectStdOutput::sync() { // Print as log as this might be verbose if (!buffer.empty()) { Base::Console().Log("%s", buffer.c_str()); buffer.clear(); } return 0; } RedirectStdLog::RedirectStdLog() { buffer.reserve(80); } int RedirectStdLog::overflow(int c) { if (c != EOF) buffer.push_back((char)c); return c; } int RedirectStdLog::sync() { // Print as log as this might be verbose if (!buffer.empty()) { Base::Console().Log("%s", buffer.c_str()); buffer.clear(); } return 0; } RedirectStdError::RedirectStdError() { buffer.reserve(80); } int RedirectStdError::overflow(int c) { if (c != EOF) buffer.push_back((char)c); return c; } int RedirectStdError::sync() { if (!buffer.empty()) { Base::Console().Error("%s", buffer.c_str()); buffer.clear(); } return 0; }