1 // Created on: 1995-02-23
2 // Created by: Remi LEQUETTE
3 // Copyright (c) 1995-1999 Matra Datavision
4 // Copyright (c) 1999-2012 OPEN CASCADE SAS
6 // The content of this file is subject to the Open CASCADE Technology Public
7 // License Version 6.5 (the "License"). You may not use the content of this file
8 // except in compliance with the License. Please obtain a copy of the License
9 // at http://www.opencascade.org and read it completely before using this file.
11 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
12 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
14 // The Original Code and all software distributed under the License is
15 // distributed on an "AS IS" basis, without warranty of any kind, and the
16 // Initial Developer hereby disclaims all such warranties, including without
17 // limitation, any warranties of merchantability, fitness for a particular
18 // purpose or non-infringement. Please see the License for the specific terms
19 // and conditions governing the rights and limitations under the License.
23 #include <Draw_Interpretor.ixx>
24 #include <Draw_Appli.hxx>
25 #include <Standard_SStream.hxx>
26 #include <Standard_RangeError.hxx>
27 #include <Standard_ErrorHandler.hxx>
28 #include <Standard_Macro.hxx>
30 #include <TCollection_AsciiString.hxx>
31 #include <TCollection_ExtendedString.hxx>
32 #include <OSD_Process.hxx>
33 #include <OSD_Path.hxx>
39 // for capturing of cout and cerr (dup(), dup2())
47 #if ! defined(STDOUT_FILENO)
48 #define STDOUT_FILENO fileno(stdout)
50 #if ! defined(STDERR_FILENO)
51 #define STDERR_FILENO fileno(stderr)
54 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)))
59 // Auxiliary tool to convert strings in command arguments from UTF-8
60 // (Tcl internal encoding since Tcl 8.1) to system local encoding,
61 // normally extended Ascii as expected by OCC commands
63 class TclUTFToLocalStringSentry {
67 TclUTFToLocalStringSentry (int argc, const char **argv) :
69 TclArgv(new Tcl_DString[argc]),
72 for (; nb < argc; nb++ ) {
73 Tcl_UtfToExternalDString ( NULL, argv[nb], -1, &TclArgv[nb] );
74 Argv[nb] = Tcl_DStringValue ( &TclArgv[nb] );
78 ~TclUTFToLocalStringSentry ()
81 while ( nb-- >0 ) Tcl_DStringFree ( &TclArgv[nb] );
85 TclUTFToLocalStringSentry (int, const char **argv) :
92 const char **GetArgv () const { return (const char **)Argv; }
102 // Call backs for TCL
106 CData(Draw_CommandFunction ff, Draw_Interpretor* ii) : f(ff), i(ii) {}
107 Draw_CommandFunction f;
113 void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
115 for (int i=0; i < argc; i++)
116 os << argv[i] << " ";
120 void flush_standard_streams ()
128 FILE* capture_start (int std_fd, int *save_fd, char*& tmp_name)
132 // open temporary files
134 // use _tempnam() to decrease chances of failure (tmpfile() creates
135 // file in root folder and will fail if it is write protected), see #24132
136 static const char* tmpdir = getenv("TEMP");
137 static char prefix[256] = ""; // prefix for temporary files, initialize once per process using pid
138 if (prefix[0] == '\0')
139 sprintf (prefix, "drawtmp%d_", (int)OSD_Process().ProcessId());
140 tmp_name = _tempnam (tmpdir, prefix);
141 FILE* aTmpFile = (tmp_name != NULL ? fopen (tmp_name, "w+b") : tmpfile());
144 FILE* aTmpFile = tmpfile();
146 int fd_tmp = (aTmpFile != NULL ? fileno (aTmpFile) : -1);
149 cerr << "Error: cannot create temporary file for capturing console output" << endl;
154 // remember current file descriptors of standard stream, and replace it by temporary
155 (*save_fd) = dup(std_fd);
156 dup2(fd_tmp, std_fd);
160 void capture_end (FILE* tmp_file, int std_fd, int save_fd, char* tmp_name, Standard_OStream &log, Standard_Boolean doEcho)
165 // restore normal descriptors of console stream
166 dup2 (save_fd, std_fd);
169 // extract all output and copy it to log and optionally to cout
170 const int BUFSIZE = 2048;
173 while (fgets (buf, BUFSIZE, tmp_file) != NULL)
180 // close temporary file
183 // remove temporary file if this is not done by the system
190 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
191 static Standard_Integer CommandCmd
192 (ClientData clientData, Tcl_Interp *interp,
193 Standard_Integer argc, const char* argv[])
195 static Standard_Integer CommandCmd
196 (ClientData clientData, Tcl_Interp *interp,
197 Standard_Integer argc, char* argv[])
200 static Standard_Integer code;
202 CData* C = (CData*) clientData;
203 Draw_Interpretor& di = *(C->i);
205 // log command execution, except commands manipulating log itself and echo
206 Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 ||
207 strcmp (argv[0], "decho") == 0);
208 Standard_Boolean doLog = (di.GetDoLog() && ! isLogManipulation);
209 Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
211 dumpArgs (di.Log(), argc, argv);
213 dumpArgs (cout, argc, argv);
215 // flush cerr and cout
216 flush_standard_streams();
218 // capture cout and cerr to log
219 char *err_name = NULL, *out_name = NULL;
220 FILE * aFile_err = NULL;
221 FILE * aFile_out = NULL;
226 aFile_out = capture_start (STDOUT_FILENO, &fd_out_save, out_name);
227 aFile_err = capture_start (STDERR_FILENO, &fd_err_save, err_name);
234 // get exception if control-break has been pressed
237 // OCC63: Convert strings from UTF-8 to local encoding, normally expected by OCC commands
238 TclUTFToLocalStringSentry anArgs ( argc, (const char**)argv );
240 Standard_Integer fres = C->f ( di, argc, anArgs.GetArgv() );
244 catch (Standard_Failure) {
246 Handle(Standard_Failure) E = Standard_Failure::Caught();
248 // fail if Draw_ExitOnCatch is set
250 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
251 const char* cc = Tcl_GetVar(interp,
252 "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
254 char* const cc = Tcl_GetVar(interp,
255 "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
258 cout << "An exception was caught " << E << endl;
260 if (cc && Draw::Atoi(cc)) {
264 Tcl_Eval(interp,"exit");
268 // get the error message
270 ss << "** Exception ** " << E << ends ;
271 #ifdef USE_STL_STREAM
272 Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
274 Tcl_SetResult(interp,(char*)(ss.str()),TCL_VOLATILE);
280 flush_standard_streams();
282 // end capturing cout and cerr
285 capture_end (aFile_err, STDERR_FILENO, fd_err_save, err_name, di.Log(), doEcho);
286 capture_end (aFile_out, STDOUT_FILENO, fd_out_save, out_name, di.Log(), doEcho);
289 // log command result
290 const char* aResultStr = NULL;
293 aResultStr = Tcl_GetStringResult (interp);
294 if (aResultStr != 0 && aResultStr[0] != '\0' )
295 di.Log() << Tcl_GetStringResult (interp) << endl;
299 if (aResultStr == NULL)
300 aResultStr = Tcl_GetStringResult (interp);
301 if (aResultStr != 0 && aResultStr[0] != '\0' )
302 cout << Tcl_GetStringResult (interp) << endl;
309 static void CommandDelete (ClientData clientData)
311 CData *C = (CData*) clientData;
315 //=======================================================================
316 //function : Draw_Interpretor
318 //=======================================================================
320 Draw_Interpretor::Draw_Interpretor() :
321 isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False)
323 // The tcl interpreter is not created immediately as it is kept
324 // by a global variable and created and deleted before the main().
328 //=======================================================================
330 //purpose : It is necessary to call this function
331 //=======================================================================
333 void Draw_Interpretor::Init()
336 Tcl_DeleteInterp(myInterp);
337 isAllocated=Standard_True;
338 myInterp=Tcl_CreateInterp();
341 //=======================================================================
342 //function : Draw_Interpretor
344 //=======================================================================
346 Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
347 isAllocated(Standard_False),
349 myDoLog(Standard_False),
350 myDoEcho(Standard_False)
354 //=======================================================================
357 //=======================================================================
359 void Draw_Interpretor::Add(const Standard_CString n,
360 const Standard_CString help,
361 const Draw_CommandFunction f,
362 const Standard_CString group)
364 //void Draw_Interpretor::Add(const Standard_CString n,
365 // const Standard_CString help,
366 // const Draw_CommandFunction& f,
367 // const Standard_CString group)
370 Standard_PCharacter pN, pHelp, pGroup;
372 pN=(Standard_PCharacter)n;
373 pHelp=(Standard_PCharacter)help;
374 pGroup=(Standard_PCharacter)group;
376 if (myInterp==NULL) Init();
378 CData* C = new CData(f,this);
380 Tcl_CreateCommand(myInterp, pN ,CommandCmd, (ClientData) C, CommandDelete);
383 Tcl_SetVar2(myInterp,"Draw_Helps", pN, pHelp, TCL_GLOBAL_ONLY);
384 Tcl_SetVar2(myInterp,"Draw_Groups",pGroup,pN,
385 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
387 //=======================================================================
390 //=======================================================================
391 void Draw_Interpretor::Add(const Standard_CString n,
392 const Standard_CString help,
393 const Standard_CString file_name,
394 const Draw_CommandFunction f,
395 const Standard_CString group)
397 Standard_PCharacter pN, pHelp, pGroup, pFileName;
399 pN=(Standard_PCharacter)n;
400 pHelp=(Standard_PCharacter)help;
401 pGroup=(Standard_PCharacter)group;
402 pFileName=(Standard_PCharacter)file_name;
404 if (myInterp==NULL) Init();
406 CData* C = new CData(f,this);
407 Tcl_CreateCommand(myInterp,pN,CommandCmd, (ClientData) C, CommandDelete);
410 Tcl_SetVar2(myInterp,"Draw_Helps",pN,pHelp,TCL_GLOBAL_ONLY);
411 Tcl_SetVar2(myInterp,"Draw_Groups",pGroup,pN,
412 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
414 // add path to source file (keep not more than two last subdirectories)
415 OSD_Path aPath (pFileName);
416 Standard_Integer nbTrek = aPath.TrekLength();
417 for (Standard_Integer i = 2; i < nbTrek; i++)
418 aPath.RemoveATrek (1);
421 TCollection_AsciiString aSrcPath;
422 aPath.SystemName (aSrcPath);
423 Tcl_SetVar2(myInterp,"Draw_Files",pN,aSrcPath.ToCString(),TCL_GLOBAL_ONLY);
427 //=======================================================================
430 //=======================================================================
432 Standard_Boolean Draw_Interpretor::Remove(Standard_CString const n)
434 Standard_PCharacter pN;
436 pN=(Standard_PCharacter)n;
438 Standard_Integer result = Tcl_DeleteCommand(myInterp,pN);
442 //=======================================================================
445 //=======================================================================
447 Standard_CString Draw_Interpretor::Result() const
449 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
450 return Tcl_GetStringResult(myInterp);
452 return myInterp->result;
456 //=======================================================================
459 //=======================================================================
461 void Draw_Interpretor::Reset()
463 Tcl_ResetResult(myInterp);
466 //=======================================================================
469 //=======================================================================
471 Draw_Interpretor& Draw_Interpretor::Append(const Standard_CString s)
474 // Convert string to UTF-8 format for Tcl
475 Tcl_DString TclString;
476 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
477 Tcl_AppendResult ( myInterp, Tcl_DStringValue ( &TclString ), (Standard_CString)0 );
478 Tcl_DStringFree ( &TclString );
480 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
485 //=======================================================================
488 //=======================================================================
490 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_AsciiString& s)
492 return Append (s.ToCString());
495 //=======================================================================
498 //=======================================================================
500 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_ExtendedString& theString)
503 // Convert string to UTF-8 format for Tcl
504 char *str = new char[theString.LengthOfCString()+1];
505 theString.ToUTF8CString (str);
506 Tcl_AppendResult ( myInterp, str, (Standard_CString)0 );
509 // put as ascii string, replacing non-ascii characters by '?'
510 TCollection_AsciiString str (theString, '?');
511 Tcl_AppendResult(myInterp,str.ToCString(),(Standard_CString)0);
516 //=======================================================================
519 //=======================================================================
521 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Integer i)
525 Tcl_AppendResult(myInterp,c,(Standard_CString)0);
529 //=======================================================================
532 //=======================================================================
534 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
537 Sprintf(s,"%.17g",r);
538 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
542 //=======================================================================
545 //=======================================================================
547 Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
549 #ifdef USE_STL_STREAM
550 return Append (s.str().c_str());
552 // Note: use dirty tricks -- unavoidable with old streams
553 TCollection_AsciiString aStr (((Standard_SStream&)AReason).str(), AReason.pcount());
554 ((Standard_SStream&)AReason).freeze (false);
555 return Append (aStr.ToCString());
559 //=======================================================================
560 //function : AppendElement
562 //=======================================================================
564 void Draw_Interpretor::AppendElement(const Standard_CString s)
567 // Convert string to UTF-8 format for Tcl
568 Tcl_DString TclString;
569 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
570 Tcl_AppendElement ( myInterp, Tcl_DStringValue ( &TclString ) );
571 Tcl_DStringFree ( &TclString );
574 //AppendElement is declared as (Tcl_Interp *interp, char *string)
576 Tcl_AppendElement(myInterp,(char*) s);
578 Tcl_AppendElement(myInterp, s);
583 //=======================================================================
586 //=======================================================================
588 Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
590 Standard_PCharacter pLine;
592 pLine=(Standard_PCharacter)line;
594 return Tcl_Eval(myInterp,pLine);
598 //=======================================================================
601 //=======================================================================
603 Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
604 const Standard_Integer flags)
606 Standard_PCharacter pLine;
608 pLine=(Standard_PCharacter)line;
609 return Tcl_RecordAndEval(myInterp,pLine,flags);
612 //=======================================================================
613 //function : EvalFile
615 //=======================================================================
617 Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
619 Standard_PCharacter pfname;
621 pfname=(Standard_PCharacter)fname;
622 return Tcl_EvalFile(myInterp,pfname);
625 //=======================================================================
628 //=======================================================================
630 Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
632 Standard_PCharacter pLine;
634 pLine=(Standard_PCharacter)line;
635 return Tcl_CommandComplete(pLine);
638 //=======================================================================
641 //=======================================================================
643 void Draw_Interpretor::Destroy()
646 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
651 catch (Standard_Failure) {
653 cout <<"Tcl_Exit have an exeption" << endl;
663 //=======================================================================
666 //=======================================================================
668 Draw_PInterp Draw_Interpretor::Interp() const
670 Standard_DomainError_Raise_if (myInterp==NULL , "No call for Draw_Interpretor::Init()");
674 void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
677 Tcl_DeleteInterp(myInterp);
678 isAllocated = Standard_False;
682 //=======================================================================
685 //=======================================================================
687 void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
692 void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
697 Standard_Boolean Draw_Interpretor::GetDoLog () const
702 Standard_Boolean Draw_Interpretor::GetDoEcho () const
707 Standard_SStream& Draw_Interpretor::Log ()