0030579: Draw Harness, Draw_Interpretor - catch exceptions other than Standard_Failure
[occt.git] / src / Draw / Draw_Interpretor.cxx
index 8244cc4..4fce4f4 100644 (file)
@@ -5,8 +5,8 @@
 //
 // This file is part of Open CASCADE Technology software library.
 //
-// This library is free software; you can redistribute it and / or modify it
-// under the terms of the GNU Lesser General Public version 2.1 as published
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
 // by the Free Software Foundation, with special exception defined in the file
 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
 // distribution for complete text of the license and disclaimer of any warranty.
@@ -14,7 +14,7 @@
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
-#include <Draw_Interpretor.ixx>
+#include <Draw_Interpretor.hxx>
 #include <Draw_Appli.hxx>
 #include <Standard_SStream.hxx>
 #include <Standard_RangeError.hxx>
 #include <OSD_Process.hxx>
 #include <OSD_Path.hxx>
 #include <OSD.hxx>
+#include <OSD_File.hxx>
 
 #include <string.h>
 #include <tcl.h>
+#include <fcntl.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
 
 // for capturing of cout and cerr (dup(), dup2())
-#ifdef _MSC_VER
+#ifdef _WIN32
 #include <io.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+#include <sys/stat.h>  
 #endif
 
 #if ! defined(STDOUT_FILENO)
 #define TCL_USES_UTF8
 #endif
 
-//
-// Auxiliary tool to convert strings in command arguments from UTF-8 
-// (Tcl internal encoding since Tcl 8.1) to system local encoding, 
-// normally extended Ascii as expected by OCC commands
-//
-class TclUTFToLocalStringSentry {
- public:
-
-#ifdef TCL_USES_UTF8
-  TclUTFToLocalStringSentry (int argc, const char **argv) :
-    nb(0),
-    TclArgv(new Tcl_DString[argc]),
-    Argv(new char*[argc])
-  {
-    for (; nb < argc; nb++ ) {
-      Tcl_UtfToExternalDString ( NULL, argv[nb], -1, &TclArgv[nb] );
-      Argv[nb] = Tcl_DStringValue ( &TclArgv[nb] );
-    }
-  }
-  
-  ~TclUTFToLocalStringSentry () 
-  {
-    delete[] Argv;
-    while ( nb-- >0 ) Tcl_DStringFree ( &TclArgv[nb] );
-    delete[] TclArgv;
-  }
-#else
-  TclUTFToLocalStringSentry (int, const char **argv) : 
-          nb(0),
-       TclArgv(NULL),
-          Argv((char**)argv)
-  {}
-#endif
-
-  const char **GetArgv () const { return (const char **)Argv; }
-  
- private:
-  int nb;
-  Tcl_DString *TclArgv;
-  char **Argv;
-};
-
-
-//
-// Call backs for TCL
-//
-
-struct CData {
-  CData(Draw_CommandFunction ff, Draw_Interpretor* ii) : f(ff), i(ii) {}
-  Draw_CommandFunction f;
-  Draw_Interpretor*    i;
-};
-
 // logging helpers
 namespace {
   void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
@@ -119,108 +69,79 @@ namespace {
     cout << flush;
   }
 
-  FILE* capture_start (int std_fd, int *save_fd, char*& tmp_name)
+  int capture_start (int theFDStd, int theFDLog)
   {
-    *save_fd = 0;
-
-    // open temporary files
-  #if defined(_WIN32)
-    // use _tempnam() to decrease chances of failure (tmpfile() creates 
-    // file in root folder and will fail if it is write protected), see #24132
-    static const char* tmpdir = getenv("TEMP");
-    static char prefix[256] = ""; // prefix for temporary files, initialize once per process using pid
-    if (prefix[0] == '\0')
-      sprintf (prefix, "drawtmp%d_", (int)OSD_Process().ProcessId());
-    tmp_name = _tempnam (tmpdir, prefix);
-    FILE* aTmpFile = (tmp_name != NULL ? fopen (tmp_name, "w+b") : tmpfile());
-  #else
-    tmp_name = NULL;
-    FILE* aTmpFile = tmpfile();
-  #endif
-    int fd_tmp = (aTmpFile != NULL ? fileno (aTmpFile) : -1);
-    if (fd_tmp < 0)
+    Standard_ASSERT_RETURN (theFDLog >= 0, "Invalid descriptor of log file", -1);
+
+    // Duplicate a file descriptor of the standard stream to be able to restore output to it later
+    int aFDSave = dup (theFDStd);
+    if (aFDSave < 0)
+    {
+      perror ("Error capturing standard stream to log: dup() returned");
+      return -1;
+    }
+
+    // Redirect the stream to the log file
+    if (dup2 (theFDLog, theFDStd) < 0)
     {
-      cerr << "Error: cannot create temporary file for capturing console output" << endl;
-      fclose (aTmpFile);
-      return NULL;
+      close (aFDSave);
+      perror ("Error capturing standard stream to log: dup2() returned");
+      return -1;
     }
 
-    // remember current file descriptors of standard stream, and replace it by temporary
-    (*save_fd) = dup(std_fd);
-    dup2(fd_tmp, std_fd);
-    return aTmpFile;
+    // remember saved file descriptor of standard stream
+    return aFDSave;
   }
 
-  void capture_end (FILE* tmp_file, int std_fd, int save_fd, char* tmp_name, Standard_OStream &log, Standard_Boolean doEcho)
+  void capture_end (int theFDStd, int& theFDSave)
   {
-    if (! tmp_file)
+    if (theFDSave < 0)
       return;
 
     // restore normal descriptors of console stream
-    dup2 (save_fd, std_fd);
-    close(save_fd);
-
-    // extract all output and copy it to log and optionally to cout
-    const int BUFSIZE = 2048;
-    char buf[BUFSIZE];
-    rewind(tmp_file);
-    while (fgets (buf, BUFSIZE, tmp_file) != NULL)
+    if (dup2(theFDSave, theFDStd) < 0)
     {
-      log << buf;
-      if (doEcho) 
-        cout << buf;
+      perror ("Error returning capturing standard stream to log: dup2() returned");
+      return;
     }
 
-    // close temporary file
-    fclose (tmp_file);
-
-    // remove temporary file if this is not done by the system
-    if (tmp_name)
-      remove (tmp_name);
+    // close saved file descriptor
+    close(theFDSave);
+    theFDSave = -1;
   }
-};
 
-// MKV 29.03.05
-#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
-static Standard_Integer CommandCmd 
-(ClientData clientData, Tcl_Interp *interp,
- Standard_Integer argc, const char* argv[])
-#else
-static Standard_Integer CommandCmd 
-(ClientData clientData, Tcl_Interp *interp,
- Standard_Integer argc, char* argv[])
-#endif
+} // anonymous namespace
+
+static Standard_Integer CommandCmd (ClientData theClientData, Tcl_Interp* interp, Standard_Integer argc, const char* argv[])
 {
   static Standard_Integer code;
   code = TCL_OK;
-  CData* C = (CData*) clientData;
-  Draw_Interpretor& di = *(C->i);
+  Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
+  Draw_Interpretor& di = *(aCallback->myDI);
 
   // log command execution, except commands manipulating log itself and echo
   Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 || 
                                         strcmp (argv[0], "decho") == 0);
   Standard_Boolean doLog  = (di.GetDoLog() && ! isLogManipulation);
   Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
-  if (doLog)
-    dumpArgs (di.Log(), argc, argv);
-  if (doEcho)
-    dumpArgs (cout, argc, argv);
 
   // flush cerr and cout
   flush_standard_streams();
 
   // capture cout and cerr to log
-  char *err_name = NULL, *out_name = NULL;
-  FILE * aFile_err = NULL;
-  FILE * aFile_out = NULL;
-  int fd_err_save = 0;
-  int fd_out_save = 0;
+  int aFDstdout = STDOUT_FILENO;
+  int aFDstderr = STDERR_FILENO;
+  int aFDerr_save = -1;
+  int aFDout_save = -1;
   if (doLog)
   {
-    aFile_out = capture_start (STDOUT_FILENO, &fd_out_save, out_name);
-    aFile_err = capture_start (STDERR_FILENO, &fd_err_save, err_name);
+    aFDout_save = capture_start (aFDstdout, di.GetLogFileDescriptor());
+    aFDerr_save = capture_start (aFDstderr, di.GetLogFileDescriptor());
   }
 
+  if (doEcho || doLog)
+    dumpArgs (cout, argc, argv);
+
   // run command
   try {
     OCC_CATCH_SIGNALS
@@ -228,31 +149,19 @@ static Standard_Integer CommandCmd
     // get exception if control-break has been pressed 
     OSD::ControlBreak();
 
-    // OCC63: Convert strings from UTF-8 to local encoding, normally expected by OCC commands
-    TclUTFToLocalStringSentry anArgs ( argc, (const char**)argv );
+    // OCC680: Transfer UTF-8 directly to OCC commands without locale usage
       
-    Standard_Integer fres = C->f ( di, argc, anArgs.GetArgv() );
+    Standard_Integer fres = aCallback->Invoke ( di, argc, argv /*anArgs.GetArgv()*/ );
     if (fres != 0) 
       code = TCL_ERROR;
   }
-  catch (Standard_Failure) {
-
-    Handle(Standard_Failure) E = Standard_Failure::Caught();
-
+  catch (Standard_Failure const& anException) {
     // fail if Draw_ExitOnCatch is set
-    // MKV 29.03.05
-#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
-    const char*  cc = Tcl_GetVar(interp,
-                         "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
-#else
-    char* const cc = Tcl_GetVar(interp,
-                         "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
-#endif
-
-    cout << "An exception was caught " << E << endl;
-
-    if (cc && Draw::Atoi(cc)) {
-#ifdef WNT
+    std::cout << "An exception was caught " << anException << std::endl;
+    const char* toExitOnCatch = Tcl_GetVar (interp, "Draw_ExitOnCatch", TCL_GLOBAL_ONLY);
+    if (toExitOnCatch != NULL && Draw::Atoi (toExitOnCatch))
+    {
+#ifdef _WIN32
       Tcl_Exit(0);
 #else      
       Tcl_Eval(interp,"exit");
@@ -261,49 +170,76 @@ static Standard_Integer CommandCmd
 
     // get the error message
     Standard_SStream ss;
-    ss << "** Exception ** " << E << ends ;
-#ifdef USE_STL_STREAM
+    ss << "** Exception ** " << anException << ends;
     Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
-#else
-    Tcl_SetResult(interp,(char*)(ss.str()),TCL_VOLATILE);
-#endif    
     code = TCL_ERROR;
   }
+  catch (std::exception const& theStdException)
+  {
+    std::cout << "An exception was caught " << theStdException.what() << " [" << typeid(theStdException).name() << "]" << std::endl;
+    const char* toExitOnCatch = Tcl_GetVar (interp, "Draw_ExitOnCatch", TCL_GLOBAL_ONLY);
+    if (toExitOnCatch != NULL && Draw::Atoi (toExitOnCatch))
+    {
+    #ifdef _WIN32
+      Tcl_Exit (0);
+    #else
+      Tcl_Eval (interp, "exit");
+    #endif
+    }
 
-  // flush streams
-  flush_standard_streams();
-
-  // end capturing cout and cerr 
-  if (doLog) 
+    // get the error message
+    Standard_SStream ss;
+    ss << "** Exception ** " << theStdException.what() << " [" << typeid(theStdException).name() << "]" << ends;
+    Tcl_SetResult (interp, (char*)(ss.str().c_str()), TCL_VOLATILE);
+    code = TCL_ERROR;
+  }
+  catch (...)
   {
-    capture_end (aFile_err, STDERR_FILENO, fd_err_save, err_name, di.Log(), doEcho);
-    capture_end (aFile_out, STDOUT_FILENO, fd_out_save, out_name, di.Log(), doEcho);
+    std::cout << "UNKNOWN exception was caught " << std::endl;
+    const char* toExitOnCatch = Tcl_GetVar (interp, "Draw_ExitOnCatch", TCL_GLOBAL_ONLY);
+    if (toExitOnCatch != NULL && Draw::Atoi (toExitOnCatch))
+    {
+    #ifdef _WIN32
+      Tcl_Exit (0);
+    #else
+      Tcl_Eval (interp,"exit");
+    #endif
+    }
+
+    // get the error message
+    Standard_SStream ss;
+    ss << "** Exception ** UNKNOWN" << ends;
+    Tcl_SetResult (interp, (char* )(ss.str().c_str()), TCL_VOLATILE);
+    code = TCL_ERROR;
   }
 
   // log command result
-  const char* aResultStr = NULL;
-  if (doLog)
+  if (doLog || doEcho)
   {
-    aResultStr = Tcl_GetStringResult (interp);
+    const char* aResultStr = Tcl_GetStringResult (interp);
     if (aResultStr != 0 && aResultStr[0] != '\0' )
-      di.Log() << Tcl_GetStringResult (interp) << endl;
+    {
+      std::cout << aResultStr << std::endl;
+    }
   }
-  if (doEcho)
+
+  // flush streams
+  flush_standard_streams();
+
+  // end capturing cout and cerr 
+  if (doLog) 
   {
-    if (aResultStr == NULL)
-      aResultStr = Tcl_GetStringResult (interp);
-    if (aResultStr != 0 && aResultStr[0] != '\0' )
-      cout << Tcl_GetStringResult (interp) << endl;
+    capture_end (aFDstderr, aFDerr_save);
+    capture_end (aFDstdout, aFDout_save);
   }
 
   return code;
 }
 
-
-static void CommandDelete (ClientData clientData)
+static void CommandDelete (ClientData theClientData)
 {
-  CData *C = (CData*) clientData;
-  delete C;
+  Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
+  delete aCallback;
 }
 
 //=======================================================================
@@ -312,7 +248,7 @@ static void CommandDelete (ClientData clientData)
 //=======================================================================
 
 Draw_Interpretor::Draw_Interpretor() :
-  isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False)
+  isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False), myFDLog(-1)
 {
 // The tcl interpreter is not created immediately as it is kept 
 // by a global variable and created and deleted before the main().
@@ -346,78 +282,49 @@ Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
 }
 
 //=======================================================================
-//function : Add
-//purpose  : 
+//function : add
+//purpose  :
 //=======================================================================
-//#ifdef WNT
-void Draw_Interpretor::Add(const Standard_CString n,
-                          const Standard_CString help,
-                          const Draw_CommandFunction f,
-                          const Standard_CString group)
-//#else
-//void Draw_Interpretor::Add(const Standard_CString n,
-//                        const Standard_CString help,
-//                        const Draw_CommandFunction& f,
-//                        const Standard_CString group)
-//#endif
+void Draw_Interpretor::add (const Standard_CString          theCommandName,
+                            const Standard_CString          theHelp,
+                            const Standard_CString          theFileName,
+                            Draw_Interpretor::CallBackData* theCallback,
+                            const Standard_CString          theGroup)
 {
-  Standard_PCharacter pN, pHelp, pGroup;
-  //
-  pN=(Standard_PCharacter)n;
-  pHelp=(Standard_PCharacter)help;
-  pGroup=(Standard_PCharacter)group;
-  //
-  if (myInterp==NULL) Init();
+  Standard_ASSERT_RAISE (myInterp != NULL, "Attempt to add command to Null interpretor");
 
-  CData* C = new CData(f,this);
-  
-  Tcl_CreateCommand(myInterp, pN ,CommandCmd, (ClientData) C, CommandDelete);
+  Standard_PCharacter aName  = (Standard_PCharacter )theCommandName;
+  Standard_PCharacter aHelp  = (Standard_PCharacter )theHelp;
+  Standard_PCharacter aGroup = (Standard_PCharacter )theGroup;
+  Tcl_CreateCommand (myInterp, aName, CommandCmd, (ClientData )theCallback, CommandDelete);
 
   // add the help
-  Tcl_SetVar2(myInterp,"Draw_Helps", pN, pHelp, TCL_GLOBAL_ONLY);
-  Tcl_SetVar2(myInterp,"Draw_Groups",pGroup,pN,
-             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
-}
-//=======================================================================
-//function : Add
-//purpose  : 
-//=======================================================================
-void Draw_Interpretor::Add(const Standard_CString n,
-                          const Standard_CString help,
-                          const Standard_CString file_name,
-                          const Draw_CommandFunction f,
-                          const Standard_CString group)
-{
-  Standard_PCharacter pN, pHelp, pGroup, pFileName;
-  //
-  pN=(Standard_PCharacter)n;
-  pHelp=(Standard_PCharacter)help;
-  pGroup=(Standard_PCharacter)group;
-  pFileName=(Standard_PCharacter)file_name;
-  //
-  if (myInterp==NULL) Init();
-
-  CData* C = new CData(f,this);
-  Tcl_CreateCommand(myInterp,pN,CommandCmd, (ClientData) C, CommandDelete);
-
-  // add the help
-  Tcl_SetVar2(myInterp,"Draw_Helps",pN,pHelp,TCL_GLOBAL_ONLY);
-  Tcl_SetVar2(myInterp,"Draw_Groups",pGroup,pN,
-             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
+  Tcl_SetVar2 (myInterp, "Draw_Helps",  aName,  aHelp, TCL_GLOBAL_ONLY);
+  Tcl_SetVar2 (myInterp, "Draw_Groups", aGroup, aName,
+                    TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
 
   // add path to source file (keep not more than two last subdirectories)
-  OSD_Path aPath (pFileName);
+  if (theFileName  == NULL
+   || *theFileName == '\0')
+  {
+    return;
+  }
+
+  OSD_Path aPath (theFileName);
   Standard_Integer nbTrek = aPath.TrekLength();
-  for (Standard_Integer i = 2; i < nbTrek; i++)
+  for (Standard_Integer i = 2; i < nbTrek; ++i)
+  {
     aPath.RemoveATrek (1);
-  aPath.SetDisk("");
-  aPath.SetNode("");
+  }
+  aPath.SetDisk ("");
+  aPath.SetNode ("");
   TCollection_AsciiString aSrcPath;
   aPath.SystemName (aSrcPath);
-  Tcl_SetVar2(myInterp,"Draw_Files",pN,aSrcPath.ToCString(),TCL_GLOBAL_ONLY);
+  if (aSrcPath.Value(1) == '/')
+    aSrcPath.Remove(1);
+  Tcl_SetVar2 (myInterp, "Draw_Files", aName, aSrcPath.ToCString(), TCL_GLOBAL_ONLY);
 }
 
-
 //=======================================================================
 //function : Remove
 //purpose  : 
@@ -540,14 +447,7 @@ Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
 
 Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
 {
-#ifdef USE_STL_STREAM
   return Append (s.str().c_str());
-#else
-  // Note: use dirty tricks -- unavoidable with old streams 
-  TCollection_AsciiString aStr (((Standard_SStream&)AReason).str(), AReason.pcount());
-  ((Standard_SStream&)AReason).freeze (false);
-  return Append (aStr.ToCString());
-#endif
 }
 
 //=======================================================================
@@ -581,11 +481,7 @@ void Draw_Interpretor::AppendElement(const Standard_CString s)
 
 Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
 {
-  Standard_PCharacter pLine;
-  //
-  pLine=(Standard_PCharacter)line;
-  //
-  return Tcl_Eval(myInterp,pLine);
+  return Tcl_Eval(myInterp,line);
 }
 
 
@@ -597,10 +493,7 @@ Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
 Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
                                                 const Standard_Integer flags)
 {
-  Standard_PCharacter pLine;
-  //
-  pLine=(Standard_PCharacter)line;
-  return Tcl_RecordAndEval(myInterp,pLine,flags);
+  return Tcl_RecordAndEval(myInterp,line,flags);
 }
 
 //=======================================================================
@@ -610,10 +503,19 @@ Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
 
 Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
 {
-  Standard_PCharacter pfname;
-  //
-  pfname=(Standard_PCharacter)fname;
-  return Tcl_EvalFile(myInterp,pfname);
+  return Tcl_EvalFile(myInterp,fname);
+}
+
+//=======================================================================
+//function : PrintHelp
+//purpose  :
+//=======================================================================
+
+Standard_Integer Draw_Interpretor::PrintHelp (const Standard_CString theCommandName)
+{
+  TCollection_AsciiString aCmd     = TCollection_AsciiString ("help ") + theCommandName;
+  Standard_PCharacter     aLinePtr = (Standard_PCharacter )aCmd.ToCString();
+  return Tcl_Eval (myInterp, aLinePtr);
 }
 
 //=======================================================================
@@ -626,7 +528,7 @@ Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
   Standard_PCharacter pLine;
   //
   pLine=(Standard_PCharacter)line;
-  return Tcl_CommandComplete(pLine);
+  return Tcl_CommandComplete (pLine) != 0;
 }
 
 //=======================================================================
@@ -634,8 +536,15 @@ Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
 //purpose  : 
 //=======================================================================
 
-void Draw_Interpretor::Destroy()
+Draw_Interpretor::~Draw_Interpretor()
 {
+  SetDoLog (Standard_False);
+  if (myFDLog >=0)
+  {
+    close (myFDLog);
+    myFDLog = 0;
+  }
+
   // MKV 01.02.05
 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
   try {
@@ -643,12 +552,12 @@ void Draw_Interpretor::Destroy()
     Tcl_Exit(0);
   }
   catch (Standard_Failure) {
-#ifdef DEB
+#ifdef OCCT_DEBUG
     cout <<"Tcl_Exit have an exeption" << endl;
 #endif
   }
 #else
-#ifdef WNT
+#ifdef _WIN32
   Tcl_Exit(0);
 #endif  
 #endif
@@ -680,6 +589,35 @@ void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
 
 void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
 {
+  if (myDoLog == doLog)
+    return;
+
+  // create log file if not opened yet
+  if (doLog && myFDLog < 0)
+  {
+#ifdef _WIN32
+    char tmpfile[L_tmpnam + 1];
+    tmpnam(tmpfile);
+    myFDLog = open (tmpfile, O_RDWR | O_CREAT | O_EXCL | O_TEMPORARY, S_IREAD | S_IWRITE);
+#else
+    // according to Linux Filesystem Hierarchy Standard, 3.17,
+    // /tmp/ is the right directory for temporary files
+    char tmpfile[256] = "/tmp/occt_draw_XXXXXX";
+    myFDLog = mkstemp (tmpfile);
+    if (myFDLog >= 0)
+    {
+//      printf ("Tmp file: %s\n", tmpfile);
+      unlink (tmpfile); // make sure the file will be deleted on close
+    }
+#endif
+    if (myFDLog < 0)
+    {
+      perror ("Error creating temporary file for capturing console output");
+      printf ("path: %s\n", tmpfile);
+      return;
+    }
+  }
+
   myDoLog = doLog;
 }
 
@@ -698,7 +636,67 @@ Standard_Boolean Draw_Interpretor::GetDoEcho () const
   return myDoEcho;
 }
 
-Standard_SStream& Draw_Interpretor::Log ()
+void Draw_Interpretor::ResetLog ()
+{
+  if (myFDLog < 0)
+    return;
+
+  // flush cerr and cout, for the case if they are bound to the log
+  flush_standard_streams();
+
+  lseek (myFDLog, 0, SEEK_SET);
+
+#ifdef _WIN32
+  if (_chsize_s (myFDLog, 0) != 0)
+#else
+  if (ftruncate (myFDLog, 0) != 0)
+#endif
+  {
+    perror ("Error truncating the console log");
+  }
+}
+
+void Draw_Interpretor::AddLog (const Standard_CString theStr)
+{
+  if (myFDLog < 0 || ! theStr || ! theStr[0])
+    return;
+
+  // flush cerr and cout, for the case if they are bound to the log
+  flush_standard_streams();
+
+  // write as plain bytes
+  if (write (myFDLog, theStr, (unsigned int)strlen(theStr)) <0)
+  {
+    perror ("Error writing to console log");
+  }
+}
+
+TCollection_AsciiString Draw_Interpretor::GetLog ()
 {
-  return myLog;
+  TCollection_AsciiString aLog;
+  if (myFDLog < 0)
+    return aLog;
+
+  // flush cerr and cout
+  flush_standard_streams();
+
+  // rewind the file to its start
+  lseek (myFDLog, 0, SEEK_SET);
+
+  // read the whole log to string; this implementation
+  // is not optimized but should be sufficient
+  const int BUFSIZE = 4096;
+  char buffer[BUFSIZE + 1];
+  for (;;)
+  {
+    int nbRead = read (myFDLog, buffer, BUFSIZE);
+    if (nbRead <= 0)
+    {
+      break;
+    }
+    buffer[nbRead] = '\0';
+    aLog.AssignCat (buffer);
+  }
+
+  return aLog;
 }