1 // Created on: 1995-02-23
2 // Created by: Remi LEQUETTE
3 // Copyright (c) 1995-1999 Matra Datavision
4 // Copyright (c) 1999-2014 OPEN CASCADE SAS
6 // This file is part of Open CASCADE Technology software library.
8 // This library is free software; you can redistribute it and/or modify it under
9 // the terms of the GNU Lesser General Public License version 2.1 as published
10 // by the Free Software Foundation, with special exception defined in the file
11 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
12 // distribution for complete text of the license and disclaimer of any warranty.
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
17 #include <Draw_Interpretor.hxx>
18 #include <Draw_Appli.hxx>
19 #include <Standard_SStream.hxx>
20 #include <Standard_RangeError.hxx>
21 #include <Standard_ErrorHandler.hxx>
22 #include <Standard_Macro.hxx>
24 #include <TCollection_AsciiString.hxx>
25 #include <TCollection_ExtendedString.hxx>
26 #include <OSD_Process.hxx>
27 #include <OSD_Path.hxx>
29 #include <OSD_File.hxx>
38 // for capturing of cout and cerr (dup(), dup2())
44 #if ! defined(STDOUT_FILENO)
45 #define STDOUT_FILENO fileno(stdout)
47 #if ! defined(STDERR_FILENO)
48 #define STDERR_FILENO fileno(stderr)
51 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)))
57 void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
59 for (int i=0; i < argc; i++)
64 void flush_standard_streams ()
68 std::cerr << std::flush;
69 std::cout << std::flush;
72 int capture_start (int theFDStd, int theFDLog)
74 Standard_ASSERT_RETURN (theFDLog >= 0, "Invalid descriptor of log file", -1);
76 // Duplicate a file descriptor of the standard stream to be able to restore output to it later
77 int aFDSave = dup (theFDStd);
80 perror ("Error capturing standard stream to log: dup() returned");
84 // Redirect the stream to the log file
85 if (dup2 (theFDLog, theFDStd) < 0)
88 perror ("Error capturing standard stream to log: dup2() returned");
92 // remember saved file descriptor of standard stream
96 void capture_end (int theFDStd, int& theFDSave)
101 // restore normal descriptors of console stream
102 if (dup2(theFDSave, theFDStd) < 0)
104 perror ("Error returning capturing standard stream to log: dup2() returned");
108 // close saved file descriptor
113 } // anonymous namespace
115 static Standard_Integer CommandCmd (ClientData theClientData, Tcl_Interp* interp, Standard_Integer argc, const char* argv[])
117 static Standard_Integer code;
119 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
120 Draw_Interpretor& di = *(aCallback->myDI);
122 // log command execution, except commands manipulating log itself and echo
123 Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 ||
124 strcmp (argv[0], "decho") == 0);
125 Standard_Boolean doLog = (di.GetDoLog() && ! isLogManipulation);
126 Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
128 // flush cerr and cout
129 flush_standard_streams();
131 // capture cout and cerr to log
132 int aFDstdout = STDOUT_FILENO;
133 int aFDstderr = STDERR_FILENO;
134 int aFDerr_save = -1;
135 int aFDout_save = -1;
138 aFDout_save = capture_start (aFDstdout, di.GetLogFileDescriptor());
139 aFDerr_save = capture_start (aFDstderr, di.GetLogFileDescriptor());
143 dumpArgs (std::cout, argc, argv);
149 // get exception if control-break has been pressed
152 // OCC680: Transfer UTF-8 directly to OCC commands without locale usage
154 Standard_Integer fres = aCallback->Invoke ( di, argc, argv /*anArgs.GetArgv()*/ );
158 catch (Standard_Failure const& anException) {
159 // fail if Draw_ExitOnCatch is set
160 std::cout << "An exception was caught " << anException << std::endl;
161 const char* toExitOnCatch = Tcl_GetVar (interp, "Draw_ExitOnCatch", TCL_GLOBAL_ONLY);
162 if (toExitOnCatch != NULL && Draw::Atoi (toExitOnCatch))
167 Tcl_Eval(interp,"exit");
171 // get the error message
173 ss << "** Exception ** " << anException << std::ends;
174 Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
177 catch (std::exception const& theStdException)
179 std::cout << "An exception was caught " << theStdException.what() << " [" << typeid(theStdException).name() << "]" << std::endl;
180 const char* toExitOnCatch = Tcl_GetVar (interp, "Draw_ExitOnCatch", TCL_GLOBAL_ONLY);
181 if (toExitOnCatch != NULL && Draw::Atoi (toExitOnCatch))
186 Tcl_Eval (interp, "exit");
190 // get the error message
192 ss << "** Exception ** " << theStdException.what() << " [" << typeid(theStdException).name() << "]" << std::ends;
193 Tcl_SetResult (interp, (char*)(ss.str().c_str()), TCL_VOLATILE);
198 std::cout << "UNKNOWN exception was caught " << std::endl;
199 const char* toExitOnCatch = Tcl_GetVar (interp, "Draw_ExitOnCatch", TCL_GLOBAL_ONLY);
200 if (toExitOnCatch != NULL && Draw::Atoi (toExitOnCatch))
205 Tcl_Eval (interp,"exit");
209 // get the error message
211 ss << "** Exception ** UNKNOWN" << std::ends;
212 Tcl_SetResult (interp, (char* )(ss.str().c_str()), TCL_VOLATILE);
216 // log command result
219 const char* aResultStr = Tcl_GetStringResult (interp);
220 if (aResultStr != 0 && aResultStr[0] != '\0' )
222 std::cout << aResultStr << std::endl;
227 flush_standard_streams();
229 // end capturing cout and cerr
232 capture_end (aFDstderr, aFDerr_save);
233 capture_end (aFDstdout, aFDout_save);
239 static void CommandDelete (ClientData theClientData)
241 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
245 //=======================================================================
246 //function : Draw_Interpretor
248 //=======================================================================
250 Draw_Interpretor::Draw_Interpretor() :
251 isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False), myFDLog(-1)
253 // The tcl interpreter is not created immediately as it is kept
254 // by a global variable and created and deleted before the main().
258 //=======================================================================
260 //purpose : It is necessary to call this function
261 //=======================================================================
263 void Draw_Interpretor::Init()
266 Tcl_DeleteInterp(myInterp);
267 isAllocated=Standard_True;
268 myInterp=Tcl_CreateInterp();
271 //=======================================================================
272 //function : Draw_Interpretor
274 //=======================================================================
276 Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
277 isAllocated(Standard_False),
279 myDoLog(Standard_False),
280 myDoEcho(Standard_False)
284 //=======================================================================
287 //=======================================================================
288 void Draw_Interpretor::add (const Standard_CString theCommandName,
289 const Standard_CString theHelp,
290 const Standard_CString theFileName,
291 Draw_Interpretor::CallBackData* theCallback,
292 const Standard_CString theGroup)
294 Standard_ASSERT_RAISE (myInterp != NULL, "Attempt to add command to Null interpretor");
296 Standard_PCharacter aName = (Standard_PCharacter )theCommandName;
297 Standard_PCharacter aHelp = (Standard_PCharacter )theHelp;
298 Standard_PCharacter aGroup = (Standard_PCharacter )theGroup;
299 Tcl_CreateCommand (myInterp, aName, CommandCmd, (ClientData )theCallback, CommandDelete);
302 Tcl_SetVar2 (myInterp, "Draw_Helps", aName, aHelp, TCL_GLOBAL_ONLY);
303 Tcl_SetVar2 (myInterp, "Draw_Groups", aGroup, aName,
304 TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
306 // add path to source file (keep not more than two last subdirectories)
307 if (theFileName == NULL
308 || *theFileName == '\0')
313 OSD_Path aPath (theFileName);
314 Standard_Integer nbTrek = aPath.TrekLength();
315 for (Standard_Integer i = 2; i < nbTrek; ++i)
317 aPath.RemoveATrek (1);
321 TCollection_AsciiString aSrcPath;
322 aPath.SystemName (aSrcPath);
323 if (aSrcPath.Value(1) == '/')
325 Tcl_SetVar2 (myInterp, "Draw_Files", aName, aSrcPath.ToCString(), TCL_GLOBAL_ONLY);
328 //=======================================================================
331 //=======================================================================
333 Standard_Boolean Draw_Interpretor::Remove(Standard_CString const n)
335 Standard_PCharacter pN;
337 pN=(Standard_PCharacter)n;
339 Standard_Integer result = Tcl_DeleteCommand(myInterp,pN);
343 //=======================================================================
346 //=======================================================================
348 Standard_CString Draw_Interpretor::Result() const
350 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
351 return Tcl_GetStringResult(myInterp);
353 return myInterp->result;
357 //=======================================================================
360 //=======================================================================
362 void Draw_Interpretor::Reset()
364 Tcl_ResetResult(myInterp);
367 //=======================================================================
370 //=======================================================================
372 Draw_Interpretor& Draw_Interpretor::Append(const Standard_CString s)
375 // Convert string to UTF-8 format for Tcl
376 Tcl_DString TclString;
377 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
378 Tcl_AppendResult ( myInterp, Tcl_DStringValue ( &TclString ), (Standard_CString)0 );
379 Tcl_DStringFree ( &TclString );
381 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
386 //=======================================================================
389 //=======================================================================
391 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_AsciiString& s)
393 return Append (s.ToCString());
396 //=======================================================================
399 //=======================================================================
401 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_ExtendedString& theString)
404 // Convert string to UTF-8 format for Tcl
405 char *str = new char[theString.LengthOfCString()+1];
406 theString.ToUTF8CString (str);
407 Tcl_AppendResult ( myInterp, str, (Standard_CString)0 );
410 // put as ascii string, replacing non-ascii characters by '?'
411 TCollection_AsciiString str (theString, '?');
412 Tcl_AppendResult(myInterp,str.ToCString(),(Standard_CString)0);
417 //=======================================================================
420 //=======================================================================
422 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Integer i)
426 Tcl_AppendResult(myInterp,c,(Standard_CString)0);
430 //=======================================================================
433 //=======================================================================
435 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
438 Sprintf(s,"%.17g",r);
439 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
443 //=======================================================================
446 //=======================================================================
448 Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
450 return Append (s.str().c_str());
453 //=======================================================================
454 //function : AppendElement
456 //=======================================================================
458 void Draw_Interpretor::AppendElement(const Standard_CString s)
461 // Convert string to UTF-8 format for Tcl
462 Tcl_DString TclString;
463 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
464 Tcl_AppendElement ( myInterp, Tcl_DStringValue ( &TclString ) );
465 Tcl_DStringFree ( &TclString );
468 //AppendElement is declared as (Tcl_Interp *interp, char *string)
470 Tcl_AppendElement(myInterp,(char*) s);
472 Tcl_AppendElement(myInterp, s);
477 //=======================================================================
480 //=======================================================================
482 Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
484 return Tcl_Eval(myInterp,line);
488 //=======================================================================
491 //=======================================================================
493 Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
494 const Standard_Integer flags)
496 return Tcl_RecordAndEval(myInterp,line,flags);
499 //=======================================================================
500 //function : EvalFile
502 //=======================================================================
504 Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
506 return Tcl_EvalFile(myInterp,fname);
509 //=======================================================================
510 //function : PrintHelp
512 //=======================================================================
514 Standard_Integer Draw_Interpretor::PrintHelp (const Standard_CString theCommandName)
516 TCollection_AsciiString aCmd = TCollection_AsciiString ("help ") + theCommandName;
517 Standard_PCharacter aLinePtr = (Standard_PCharacter )aCmd.ToCString();
518 return Tcl_Eval (myInterp, aLinePtr);
521 //=======================================================================
524 //=======================================================================
526 Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
528 Standard_PCharacter pLine;
530 pLine=(Standard_PCharacter)line;
531 return Tcl_CommandComplete (pLine) != 0;
534 //=======================================================================
537 //=======================================================================
539 Draw_Interpretor::~Draw_Interpretor()
541 SetDoLog (Standard_False);
549 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
554 catch (Standard_Failure const&) {
556 std::cout <<"Tcl_Exit have an exeption" << std::endl;
566 //=======================================================================
569 //=======================================================================
571 Draw_PInterp Draw_Interpretor::Interp() const
573 Standard_DomainError_Raise_if (myInterp==NULL , "No call for Draw_Interpretor::Init()");
577 void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
580 Tcl_DeleteInterp(myInterp);
581 isAllocated = Standard_False;
585 //=======================================================================
588 //=======================================================================
590 void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
592 if (myDoLog == doLog)
595 // create log file if not opened yet
596 if (doLog && myFDLog < 0)
599 char tmpfile[L_tmpnam + 1];
601 myFDLog = open (tmpfile, O_RDWR | O_CREAT | O_EXCL | O_TEMPORARY, S_IREAD | S_IWRITE);
603 // according to Linux Filesystem Hierarchy Standard, 3.17,
604 // /tmp/ is the right directory for temporary files
605 char tmpfile[256] = "/tmp/occt_draw_XXXXXX";
606 myFDLog = mkstemp (tmpfile);
609 // printf ("Tmp file: %s\n", tmpfile);
610 unlink (tmpfile); // make sure the file will be deleted on close
615 perror ("Error creating temporary file for capturing console output");
616 printf ("path: %s\n", tmpfile);
624 void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
629 Standard_Boolean Draw_Interpretor::GetDoLog () const
634 Standard_Boolean Draw_Interpretor::GetDoEcho () const
639 void Draw_Interpretor::ResetLog ()
644 // flush cerr and cout, for the case if they are bound to the log
645 flush_standard_streams();
647 lseek (myFDLog, 0, SEEK_SET);
650 if (_chsize_s (myFDLog, 0) != 0)
652 if (ftruncate (myFDLog, 0) != 0)
655 perror ("Error truncating the console log");
659 void Draw_Interpretor::AddLog (const Standard_CString theStr)
661 if (myFDLog < 0 || ! theStr || ! theStr[0])
664 // flush cerr and cout, for the case if they are bound to the log
665 flush_standard_streams();
667 // write as plain bytes
668 if (write (myFDLog, theStr, (unsigned int)strlen(theStr)) <0)
670 perror ("Error writing to console log");
674 TCollection_AsciiString Draw_Interpretor::GetLog ()
676 TCollection_AsciiString aLog;
680 // flush cerr and cout
681 flush_standard_streams();
683 // rewind the file to its start
684 lseek (myFDLog, 0, SEEK_SET);
686 // read the whole log to string; this implementation
687 // is not optimized but should be sufficient
688 const int BUFSIZE = 4096;
689 char buffer[BUFSIZE + 1];
692 int nbRead = read (myFDLog, buffer, BUFSIZE);
697 buffer[nbRead] = '\0';
698 aLog.AssignCat (buffer);