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>
36 // for capturing of cout and cerr (dup(), dup2())
41 #if ! defined(STDOUT_FILENO)
42 #define STDOUT_FILENO fileno(stdout)
44 #if ! defined(STDERR_FILENO)
45 #define STDERR_FILENO fileno(stderr)
48 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)))
54 void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
56 for (int i=0; i < argc; i++)
61 void flush_standard_streams ()
69 FILE* capture_start (int std_fd, int *save_fd, char*& tmp_name)
73 // open temporary files
75 // use _tempnam() to decrease chances of failure (tmpfile() creates
76 // file in root folder and will fail if it is write protected), see #24132
77 static const char* tmpdir = getenv("TEMP");
78 static char prefix[256] = ""; // prefix for temporary files, initialize once per process using pid
79 if (prefix[0] == '\0')
80 sprintf (prefix, "drawtmp%d_", (int)OSD_Process().ProcessId());
81 tmp_name = _tempnam (tmpdir, prefix);
82 FILE* aTmpFile = (tmp_name != NULL ? fopen (tmp_name, "w+b") : tmpfile());
85 FILE* aTmpFile = tmpfile();
87 int fd_tmp = (aTmpFile != NULL ? fileno (aTmpFile) : -1);
90 cerr << "Error: cannot create temporary file for capturing console output" << endl;
95 // remember current file descriptors of standard stream, and replace it by temporary
96 (*save_fd) = dup(std_fd);
101 void capture_end (FILE* tmp_file, int std_fd, int save_fd, char* tmp_name, Standard_OStream &log, Standard_Boolean doEcho)
106 // restore normal descriptors of console stream
107 dup2 (save_fd, std_fd);
110 // extract all output and copy it to log and optionally to cout
111 const int BUFSIZE = 2048;
114 while (fgets (buf, BUFSIZE, tmp_file) != NULL)
121 // close temporary file
124 // remove temporary file if this is not done by the system
131 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
132 static Standard_Integer CommandCmd
133 (ClientData theClientData, Tcl_Interp *interp,
134 Standard_Integer argc, const char* argv[])
136 static Standard_Integer CommandCmd
137 (ClientData theClientData, Tcl_Interp *interp,
138 Standard_Integer argc, char* argv[])
141 static Standard_Integer code;
143 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
144 Draw_Interpretor& di = *(aCallback->myDI);
146 // log command execution, except commands manipulating log itself and echo
147 Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 ||
148 strcmp (argv[0], "decho") == 0);
149 Standard_Boolean doLog = (di.GetDoLog() && ! isLogManipulation);
150 Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
152 dumpArgs (di.Log(), argc, argv);
154 dumpArgs (cout, argc, argv);
156 // flush cerr and cout
157 flush_standard_streams();
159 // capture cout and cerr to log
160 char *err_name = NULL, *out_name = NULL;
161 FILE * aFile_err = NULL;
162 FILE * aFile_out = NULL;
167 aFile_out = capture_start (STDOUT_FILENO, &fd_out_save, out_name);
168 aFile_err = capture_start (STDERR_FILENO, &fd_err_save, err_name);
175 // get exception if control-break has been pressed
178 // OCC680: Transfer UTF-8 directly to OCC commands without locale usage
180 Standard_Integer fres = aCallback->Invoke ( di, argc, argv /*anArgs.GetArgv()*/ );
184 catch (Standard_Failure) {
186 Handle(Standard_Failure) E = Standard_Failure::Caught();
188 // fail if Draw_ExitOnCatch is set
190 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
191 const char* cc = Tcl_GetVar(interp,
192 "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
194 char* const cc = Tcl_GetVar(interp,
195 "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
198 cout << "An exception was caught " << E << endl;
200 if (cc && Draw::Atoi(cc)) {
204 Tcl_Eval(interp,"exit");
208 // get the error message
210 ss << "** Exception ** " << E << ends;
211 Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
216 flush_standard_streams();
218 // end capturing cout and cerr
221 capture_end (aFile_err, STDERR_FILENO, fd_err_save, err_name, di.Log(), doEcho);
222 capture_end (aFile_out, STDOUT_FILENO, fd_out_save, out_name, di.Log(), doEcho);
225 // log command result
226 const char* aResultStr = NULL;
229 aResultStr = Tcl_GetStringResult (interp);
230 if (aResultStr != 0 && aResultStr[0] != '\0' )
231 di.Log() << Tcl_GetStringResult (interp) << endl;
235 if (aResultStr == NULL)
236 aResultStr = Tcl_GetStringResult (interp);
237 if (aResultStr != 0 && aResultStr[0] != '\0' )
238 cout << Tcl_GetStringResult (interp) << endl;
244 static void CommandDelete (ClientData theClientData)
246 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
250 //=======================================================================
251 //function : Draw_Interpretor
253 //=======================================================================
255 Draw_Interpretor::Draw_Interpretor() :
256 isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False)
258 // The tcl interpreter is not created immediately as it is kept
259 // by a global variable and created and deleted before the main().
263 //=======================================================================
265 //purpose : It is necessary to call this function
266 //=======================================================================
268 void Draw_Interpretor::Init()
271 Tcl_DeleteInterp(myInterp);
272 isAllocated=Standard_True;
273 myInterp=Tcl_CreateInterp();
276 //=======================================================================
277 //function : Draw_Interpretor
279 //=======================================================================
281 Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
282 isAllocated(Standard_False),
284 myDoLog(Standard_False),
285 myDoEcho(Standard_False)
289 //=======================================================================
292 //=======================================================================
293 void Draw_Interpretor::add (const Standard_CString theCommandName,
294 const Standard_CString theHelp,
295 const Standard_CString theFileName,
296 Draw_Interpretor::CallBackData* theCallback,
297 const Standard_CString theGroup)
299 if (myInterp == NULL)
304 Standard_PCharacter aName = (Standard_PCharacter )theCommandName;
305 Standard_PCharacter aHelp = (Standard_PCharacter )theHelp;
306 Standard_PCharacter aGroup = (Standard_PCharacter )theGroup;
307 Tcl_CreateCommand (myInterp, aName, CommandCmd, (ClientData )theCallback, CommandDelete);
310 Tcl_SetVar2 (myInterp, "Draw_Helps", aName, aHelp, TCL_GLOBAL_ONLY);
311 Tcl_SetVar2 (myInterp, "Draw_Groups", aGroup, aName,
312 TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
314 // add path to source file (keep not more than two last subdirectories)
315 if (theFileName == NULL
316 || *theFileName == '\0')
321 OSD_Path aPath (theFileName);
322 Standard_Integer nbTrek = aPath.TrekLength();
323 for (Standard_Integer i = 2; i < nbTrek; ++i)
325 aPath.RemoveATrek (1);
329 TCollection_AsciiString aSrcPath;
330 aPath.SystemName (aSrcPath);
331 Tcl_SetVar2 (myInterp, "Draw_Files", aName, aSrcPath.ToCString(), TCL_GLOBAL_ONLY);
334 //=======================================================================
337 //=======================================================================
339 Standard_Boolean Draw_Interpretor::Remove(Standard_CString const n)
341 Standard_PCharacter pN;
343 pN=(Standard_PCharacter)n;
345 Standard_Integer result = Tcl_DeleteCommand(myInterp,pN);
349 //=======================================================================
352 //=======================================================================
354 Standard_CString Draw_Interpretor::Result() const
356 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
357 return Tcl_GetStringResult(myInterp);
359 return myInterp->result;
363 //=======================================================================
366 //=======================================================================
368 void Draw_Interpretor::Reset()
370 Tcl_ResetResult(myInterp);
373 //=======================================================================
376 //=======================================================================
378 Draw_Interpretor& Draw_Interpretor::Append(const Standard_CString s)
381 // Convert string to UTF-8 format for Tcl
382 Tcl_DString TclString;
383 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
384 Tcl_AppendResult ( myInterp, Tcl_DStringValue ( &TclString ), (Standard_CString)0 );
385 Tcl_DStringFree ( &TclString );
387 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
392 //=======================================================================
395 //=======================================================================
397 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_AsciiString& s)
399 return Append (s.ToCString());
402 //=======================================================================
405 //=======================================================================
407 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_ExtendedString& theString)
410 // Convert string to UTF-8 format for Tcl
411 char *str = new char[theString.LengthOfCString()+1];
412 theString.ToUTF8CString (str);
413 Tcl_AppendResult ( myInterp, str, (Standard_CString)0 );
416 // put as ascii string, replacing non-ascii characters by '?'
417 TCollection_AsciiString str (theString, '?');
418 Tcl_AppendResult(myInterp,str.ToCString(),(Standard_CString)0);
423 //=======================================================================
426 //=======================================================================
428 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Integer i)
432 Tcl_AppendResult(myInterp,c,(Standard_CString)0);
436 //=======================================================================
439 //=======================================================================
441 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
444 Sprintf(s,"%.17g",r);
445 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
449 //=======================================================================
452 //=======================================================================
454 Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
456 return Append (s.str().c_str());
459 //=======================================================================
460 //function : AppendElement
462 //=======================================================================
464 void Draw_Interpretor::AppendElement(const Standard_CString s)
467 // Convert string to UTF-8 format for Tcl
468 Tcl_DString TclString;
469 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
470 Tcl_AppendElement ( myInterp, Tcl_DStringValue ( &TclString ) );
471 Tcl_DStringFree ( &TclString );
474 //AppendElement is declared as (Tcl_Interp *interp, char *string)
476 Tcl_AppendElement(myInterp,(char*) s);
478 Tcl_AppendElement(myInterp, s);
483 //=======================================================================
486 //=======================================================================
488 Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
490 Standard_PCharacter pLine;
492 pLine=(Standard_PCharacter)line;
494 return Tcl_Eval(myInterp,pLine);
498 //=======================================================================
501 //=======================================================================
503 Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
504 const Standard_Integer flags)
506 Standard_PCharacter pLine;
508 pLine=(Standard_PCharacter)line;
509 return Tcl_RecordAndEval(myInterp,pLine,flags);
512 //=======================================================================
513 //function : EvalFile
515 //=======================================================================
517 Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
519 Standard_PCharacter pfname;
521 pfname=(Standard_PCharacter)fname;
522 return Tcl_EvalFile(myInterp,pfname);
525 //=======================================================================
526 //function : PrintHelp
528 //=======================================================================
530 Standard_Integer Draw_Interpretor::PrintHelp (const Standard_CString theCommandName)
532 TCollection_AsciiString aCmd = TCollection_AsciiString ("help ") + theCommandName;
533 Standard_PCharacter aLinePtr = (Standard_PCharacter )aCmd.ToCString();
534 return Tcl_Eval (myInterp, aLinePtr);
537 //=======================================================================
540 //=======================================================================
542 Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
544 Standard_PCharacter pLine;
546 pLine=(Standard_PCharacter)line;
547 return Tcl_CommandComplete(pLine);
550 //=======================================================================
553 //=======================================================================
555 Draw_Interpretor::~Draw_Interpretor()
558 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
563 catch (Standard_Failure) {
565 cout <<"Tcl_Exit have an exeption" << endl;
575 //=======================================================================
578 //=======================================================================
580 Draw_PInterp Draw_Interpretor::Interp() const
582 Standard_DomainError_Raise_if (myInterp==NULL , "No call for Draw_Interpretor::Init()");
586 void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
589 Tcl_DeleteInterp(myInterp);
590 isAllocated = Standard_False;
594 //=======================================================================
597 //=======================================================================
599 void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
604 void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
609 Standard_Boolean Draw_Interpretor::GetDoLog () const
614 Standard_Boolean Draw_Interpretor::GetDoEcho () const
619 Standard_SStream& Draw_Interpretor::Log ()