//
// 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.
// 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[])
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
// 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");
// 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;
}
//=======================================================================
//=======================================================================
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().
}
//=======================================================================
-//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 :
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
}
//=======================================================================
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);
}
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);
}
//=======================================================================
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);
}
//=======================================================================
Standard_PCharacter pLine;
//
pLine=(Standard_PCharacter)line;
- return Tcl_CommandComplete(pLine);
+ return Tcl_CommandComplete (pLine) != 0;
}
//=======================================================================
//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 {
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
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;
}
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;
}