0027620: Test perf bop boxholes crashes DRAW
[occt.git] / src / Draw / Draw_Interpretor.cxx
CommitLineData
b311480e 1// Created on: 1995-02-23
2// Created by: Remi LEQUETTE
3// Copyright (c) 1995-1999 Matra Datavision
973c2be1 4// Copyright (c) 1999-2014 OPEN CASCADE SAS
b311480e 5//
973c2be1 6// This file is part of Open CASCADE Technology software library.
b311480e 7//
d5f74e42 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
973c2be1 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.
b311480e 13//
973c2be1 14// Alternatively, this file may be used under the terms of Open CASCADE
15// commercial license or contractual agreement.
7fd59977 16
dda67c1c 17#include <Draw_Interpretor.hxx>
7fd59977 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>
23
24#include <TCollection_AsciiString.hxx>
25#include <TCollection_ExtendedString.hxx>
e9b037ef 26#include <OSD_Process.hxx>
d33dea30 27#include <OSD_Path.hxx>
8a262fa1 28#include <OSD.hxx>
95e05159 29#include <OSD_File.hxx>
7fd59977 30
31#include <string.h>
7fd59977 32#include <tcl.h>
e05c25c1 33#include <fcntl.h>
03155c18 34#ifndef _WIN32
35#include <unistd.h>
36#endif
7fd59977 37
aa02980d 38// for capturing of cout and cerr (dup(), dup2())
7c65581d 39#ifdef _WIN32
aa02980d 40#include <io.h>
e05c25c1 41#include <sys/stat.h>
aa02980d 42#endif
aa02980d 43
44#if ! defined(STDOUT_FILENO)
45#define STDOUT_FILENO fileno(stdout)
46#endif
47#if ! defined(STDERR_FILENO)
48#define STDERR_FILENO fileno(stderr)
49#endif
50
7fd59977 51#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)))
52#define TCL_USES_UTF8
53#endif
54
aa02980d 55// logging helpers
56namespace {
57 void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
58 {
59 for (int i=0; i < argc; i++)
60 os << argv[i] << " ";
61 os << endl;
62 }
63
64 void flush_standard_streams ()
65 {
66 fflush (stderr);
67 fflush (stdout);
68 cerr << flush;
69 cout << flush;
70 }
71
e05c25c1 72 int capture_start (int theFDStd, int theFDLog)
aa02980d 73 {
e05c25c1 74 Standard_ASSERT_RETURN (theFDLog >= 0, "Invalid descriptor of log file", -1);
75
76 // Duplicate a file descriptor of the standard stream to be able to restore output to it later
77 int aFDSave = dup (theFDStd);
78 if (aFDSave < 0)
79 {
80 perror ("Error capturing standard stream to log: dup() returned");
81 return -1;
82 }
83
84 // Redirect the stream to the log file
85 if (dup2 (theFDLog, theFDStd) < 0)
aa02980d 86 {
e05c25c1 87 close (aFDSave);
88 perror ("Error capturing standard stream to log: dup2() returned");
95e05159 89 return -1;
aa02980d 90 }
91
e05c25c1 92 // remember saved file descriptor of standard stream
93 return aFDSave;
aa02980d 94 }
95
e05c25c1 96 void capture_end (int theFDStd, int& theFDSave)
aa02980d 97 {
e05c25c1 98 if (theFDSave < 0)
e9b037ef 99 return;
100
aa02980d 101 // restore normal descriptors of console stream
e05c25c1 102 if (dup2(theFDSave, theFDStd) < 0)
aa02980d 103 {
e05c25c1 104 perror ("Error returning capturing standard stream to log: dup2() returned");
105 return;
aa02980d 106 }
107
e05c25c1 108 // close saved file descriptor
109 close(theFDSave);
110 theFDSave = -1;
aa02980d 111 }
95e05159 112
68858c7d 113} // anonymous namespace
aa02980d 114
7fd59977 115// MKV 29.03.05
116#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
117static Standard_Integer CommandCmd
dda67c1c 118(ClientData theClientData, Tcl_Interp *interp,
7fd59977 119 Standard_Integer argc, const char* argv[])
120#else
121static Standard_Integer CommandCmd
dda67c1c 122(ClientData theClientData, Tcl_Interp *interp,
7fd59977 123 Standard_Integer argc, char* argv[])
124#endif
125{
126 static Standard_Integer code;
127 code = TCL_OK;
dda67c1c 128 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
129 Draw_Interpretor& di = *(aCallback->myDI);
aa02980d 130
131 // log command execution, except commands manipulating log itself and echo
132 Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 ||
133 strcmp (argv[0], "decho") == 0);
134 Standard_Boolean doLog = (di.GetDoLog() && ! isLogManipulation);
135 Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
aa02980d 136
137 // flush cerr and cout
138 flush_standard_streams();
139
140 // capture cout and cerr to log
e05c25c1 141 int aFDstdout = STDOUT_FILENO;
142 int aFDstderr = STDERR_FILENO;
143 int aFDerr_save = -1;
144 int aFDout_save = -1;
aa02980d 145 if (doLog)
146 {
e05c25c1 147 aFDout_save = capture_start (aFDstdout, di.GetLogFileDescriptor());
148 aFDerr_save = capture_start (aFDstderr, di.GetLogFileDescriptor());
aa02980d 149 }
7fd59977 150
e05c25c1 151 if (doEcho || doLog)
152 dumpArgs (cout, argc, argv);
153
aa02980d 154 // run command
7fd59977 155 try {
156 OCC_CATCH_SIGNALS
157
8a262fa1 158 // get exception if control-break has been pressed
159 OSD::ControlBreak();
160
d9ff84e8 161 // OCC680: Transfer UTF-8 directly to OCC commands without locale usage
7fd59977 162
d9ff84e8 163 Standard_Integer fres = aCallback->Invoke ( di, argc, argv /*anArgs.GetArgv()*/ );
7fd59977 164 if (fres != 0)
165 code = TCL_ERROR;
166 }
9775fa61 167 catch (Standard_Failure const& anException) {
7fd59977 168 // fail if Draw_ExitOnCatch is set
169 // MKV 29.03.05
170#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
171 const char* cc = Tcl_GetVar(interp,
172 "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
173#else
174 char* const cc = Tcl_GetVar(interp,
175 "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
176#endif
177
9775fa61 178 cout << "An exception was caught " << anException << endl;
7fd59977 179
91322f44 180 if (cc && Draw::Atoi(cc)) {
57c28b61 181#ifdef _WIN32
7fd59977 182 Tcl_Exit(0);
183#else
184 Tcl_Eval(interp,"exit");
185#endif
186 }
187
188 // get the error message
189 Standard_SStream ss;
9775fa61 190 ss << "** Exception ** " << anException << ends;
7fd59977 191 Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
7fd59977 192 code = TCL_ERROR;
193 }
aa02980d 194
e05c25c1 195 // log command result
196 if (doLog || doEcho)
197 {
198 const char* aResultStr = Tcl_GetStringResult (interp);
199 if (aResultStr != 0 && aResultStr[0] != '\0' )
200 {
201 std::cout << aResultStr << std::endl;
202 }
203 }
204
aa02980d 205 // flush streams
206 flush_standard_streams();
207
208 // end capturing cout and cerr
209 if (doLog)
210 {
e05c25c1 211 capture_end (aFDstderr, aFDerr_save);
212 capture_end (aFDstdout, aFDout_save);
aa02980d 213 }
214
7fd59977 215 return code;
216}
217
dda67c1c 218static void CommandDelete (ClientData theClientData)
7fd59977 219{
dda67c1c 220 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
221 delete aCallback;
7fd59977 222}
223
224//=======================================================================
225//function : Draw_Interpretor
226//purpose :
227//=======================================================================
228
229Draw_Interpretor::Draw_Interpretor() :
e05c25c1 230 isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False), myFDLog(-1)
7fd59977 231{
0d969553
Y
232// The tcl interpreter is not created immediately as it is kept
233// by a global variable and created and deleted before the main().
7fd59977 234 myInterp = NULL;
235}
236
237//=======================================================================
238//function : Init
0d969553 239//purpose : It is necessary to call this function
7fd59977 240//=======================================================================
241
242void Draw_Interpretor::Init()
243{
244 if (isAllocated)
245 Tcl_DeleteInterp(myInterp);
246 isAllocated=Standard_True;
247 myInterp=Tcl_CreateInterp();
248}
249
250//=======================================================================
251//function : Draw_Interpretor
252//purpose :
253//=======================================================================
254
255Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
256 isAllocated(Standard_False),
aa02980d 257 myInterp(p),
258 myDoLog(Standard_False),
259 myDoEcho(Standard_False)
7fd59977 260{
261}
262
263//=======================================================================
dda67c1c 264//function : add
265//purpose :
7fd59977 266//=======================================================================
dda67c1c 267void Draw_Interpretor::add (const Standard_CString theCommandName,
268 const Standard_CString theHelp,
269 const Standard_CString theFileName,
270 Draw_Interpretor::CallBackData* theCallback,
271 const Standard_CString theGroup)
7fd59977 272{
dda67c1c 273 if (myInterp == NULL)
274 {
275 Init();
276 }
7fd59977 277
dda67c1c 278 Standard_PCharacter aName = (Standard_PCharacter )theCommandName;
279 Standard_PCharacter aHelp = (Standard_PCharacter )theHelp;
280 Standard_PCharacter aGroup = (Standard_PCharacter )theGroup;
281 Tcl_CreateCommand (myInterp, aName, CommandCmd, (ClientData )theCallback, CommandDelete);
7fd59977 282
283 // add the help
dda67c1c 284 Tcl_SetVar2 (myInterp, "Draw_Helps", aName, aHelp, TCL_GLOBAL_ONLY);
285 Tcl_SetVar2 (myInterp, "Draw_Groups", aGroup, aName,
286 TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
938a360f 287
d33dea30 288 // add path to source file (keep not more than two last subdirectories)
dda67c1c 289 if (theFileName == NULL
290 || *theFileName == '\0')
291 {
292 return;
293 }
294
295 OSD_Path aPath (theFileName);
d33dea30 296 Standard_Integer nbTrek = aPath.TrekLength();
dda67c1c 297 for (Standard_Integer i = 2; i < nbTrek; ++i)
298 {
d33dea30 299 aPath.RemoveATrek (1);
dda67c1c 300 }
301 aPath.SetDisk ("");
302 aPath.SetNode ("");
d33dea30
PK
303 TCollection_AsciiString aSrcPath;
304 aPath.SystemName (aSrcPath);
36cc2619 305 if (aSrcPath.Value(1) == '/')
306 aSrcPath.Remove(1);
dda67c1c 307 Tcl_SetVar2 (myInterp, "Draw_Files", aName, aSrcPath.ToCString(), TCL_GLOBAL_ONLY);
7fd59977 308}
309
7fd59977 310//=======================================================================
311//function : Remove
312//purpose :
313//=======================================================================
314
315Standard_Boolean Draw_Interpretor::Remove(Standard_CString const n)
316{
317 Standard_PCharacter pN;
318 //
319 pN=(Standard_PCharacter)n;
320
321 Standard_Integer result = Tcl_DeleteCommand(myInterp,pN);
322 return result == 0;
323}
324
325//=======================================================================
326//function : Result
327//purpose :
328//=======================================================================
329
330Standard_CString Draw_Interpretor::Result() const
331{
332#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
333 return Tcl_GetStringResult(myInterp);
334#else
335 return myInterp->result;
336#endif
337}
338
339//=======================================================================
340//function : Reset
341//purpose :
342//=======================================================================
343
344void Draw_Interpretor::Reset()
345{
346 Tcl_ResetResult(myInterp);
347}
348
349//=======================================================================
350//function : Append
351//purpose :
352//=======================================================================
353
354Draw_Interpretor& Draw_Interpretor::Append(const Standard_CString s)
355{
356#ifdef TCL_USES_UTF8
357 // Convert string to UTF-8 format for Tcl
358 Tcl_DString TclString;
359 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
360 Tcl_AppendResult ( myInterp, Tcl_DStringValue ( &TclString ), (Standard_CString)0 );
361 Tcl_DStringFree ( &TclString );
362#else
363 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
364#endif
365 return *this;
366}
367
368//=======================================================================
369//function : Append
370//purpose :
371//=======================================================================
372
373Draw_Interpretor& Draw_Interpretor::Append(const TCollection_AsciiString& s)
374{
375 return Append (s.ToCString());
376}
377
378//=======================================================================
379//function : Append
380//purpose :
381//=======================================================================
382
383Draw_Interpretor& Draw_Interpretor::Append(const TCollection_ExtendedString& theString)
384{
385#ifdef TCL_USES_UTF8
386 // Convert string to UTF-8 format for Tcl
387 char *str = new char[theString.LengthOfCString()+1];
388 theString.ToUTF8CString (str);
389 Tcl_AppendResult ( myInterp, str, (Standard_CString)0 );
390 delete[] str;
391#else
392 // put as ascii string, replacing non-ascii characters by '?'
393 TCollection_AsciiString str (theString, '?');
394 Tcl_AppendResult(myInterp,str.ToCString(),(Standard_CString)0);
395#endif
396 return *this;
397}
398
399//=======================================================================
400//function : Append
401//purpose :
402//=======================================================================
403
404Draw_Interpretor& Draw_Interpretor::Append(const Standard_Integer i)
405{
406 char c[100];
91322f44 407 Sprintf(c,"%d",i);
7fd59977 408 Tcl_AppendResult(myInterp,c,(Standard_CString)0);
409 return *this;
410}
411
412//=======================================================================
413//function : Append
414//purpose :
415//=======================================================================
416
417Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
418{
419 char s[100];
91322f44 420 Sprintf(s,"%.17g",r);
7fd59977 421 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
422 return *this;
423}
424
425//=======================================================================
426//function : Append
427//purpose :
428//=======================================================================
429
430Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
431{
7fd59977 432 return Append (s.str().c_str());
7fd59977 433}
434
435//=======================================================================
436//function : AppendElement
437//purpose :
438//=======================================================================
439
440void Draw_Interpretor::AppendElement(const Standard_CString s)
441{
442#ifdef TCL_USES_UTF8
443 // Convert string to UTF-8 format for Tcl
444 Tcl_DString TclString;
445 Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
446 Tcl_AppendElement ( myInterp, Tcl_DStringValue ( &TclString ) );
447 Tcl_DStringFree ( &TclString );
448#else
449#ifdef IRIX
450 //AppendElement is declared as (Tcl_Interp *interp, char *string)
451 //on SGI 32
452 Tcl_AppendElement(myInterp,(char*) s);
453#else
454 Tcl_AppendElement(myInterp, s);
455#endif
456#endif
457}
458
459//=======================================================================
460//function : Eval
461//purpose :
462//=======================================================================
463
464Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
465{
a2f76b15 466 return Tcl_Eval(myInterp,line);
7fd59977 467}
468
469
470//=======================================================================
471//function : Eval
472//purpose :
473//=======================================================================
474
475Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
476 const Standard_Integer flags)
477{
a2f76b15 478 return Tcl_RecordAndEval(myInterp,line,flags);
7fd59977 479}
480
481//=======================================================================
482//function : EvalFile
483//purpose :
484//=======================================================================
485
486Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
487{
a2f76b15 488 return Tcl_EvalFile(myInterp,fname);
7fd59977 489}
490
785a9540 491//=======================================================================
492//function : PrintHelp
493//purpose :
494//=======================================================================
495
496Standard_Integer Draw_Interpretor::PrintHelp (const Standard_CString theCommandName)
497{
498 TCollection_AsciiString aCmd = TCollection_AsciiString ("help ") + theCommandName;
499 Standard_PCharacter aLinePtr = (Standard_PCharacter )aCmd.ToCString();
500 return Tcl_Eval (myInterp, aLinePtr);
501}
502
7fd59977 503//=======================================================================
504//function :Complete
505//purpose :
506//=======================================================================
507
508Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
509{
510 Standard_PCharacter pLine;
511 //
512 pLine=(Standard_PCharacter)line;
dde68833 513 return Tcl_CommandComplete (pLine) != 0;
7fd59977 514}
515
516//=======================================================================
517//function : Destroy
518//purpose :
519//=======================================================================
520
dda67c1c 521Draw_Interpretor::~Draw_Interpretor()
7fd59977 522{
e05c25c1 523 SetDoLog (Standard_False);
524 if (myFDLog >=0)
525 {
526 close (myFDLog);
527 myFDLog = 0;
528 }
529
7fd59977 530 // MKV 01.02.05
531#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
532 try {
533 OCC_CATCH_SIGNALS
534 Tcl_Exit(0);
535 }
536 catch (Standard_Failure) {
0797d9d3 537#ifdef OCCT_DEBUG
7fd59977 538 cout <<"Tcl_Exit have an exeption" << endl;
539#endif
540 }
541#else
57c28b61 542#ifdef _WIN32
7fd59977 543 Tcl_Exit(0);
544#endif
545#endif
546}
547
548//=======================================================================
549//function : Interp
550//purpose :
551//=======================================================================
552
553Draw_PInterp Draw_Interpretor::Interp() const
554{
555 Standard_DomainError_Raise_if (myInterp==NULL , "No call for Draw_Interpretor::Init()");
556 return myInterp;
557}
558
559void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
560{
561 if (isAllocated)
562 Tcl_DeleteInterp(myInterp);
563 isAllocated = Standard_False;
564 myInterp = PIntrp;
565}
aa02980d 566
567//=======================================================================
568//function : Logging
569//purpose :
570//=======================================================================
571
572void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
573{
e05c25c1 574 if (myDoLog == doLog)
575 return;
576
577 // create log file if not opened yet
578 if (doLog && myFDLog < 0)
579 {
580#ifdef _WIN32
581 char tmpfile[L_tmpnam + 1];
582 tmpnam(tmpfile);
583 myFDLog = open (tmpfile, O_RDWR | O_CREAT | O_EXCL | O_TEMPORARY, S_IREAD | S_IWRITE);
584#else
585 // according to Linux Filesystem Hierarchy Standard, 3.17,
586 // /tmp/ is the right directory for temporary files
587 char tmpfile[256] = "/tmp/occt_draw_XXXXXX";
588 myFDLog = mkstemp (tmpfile);
589 if (myFDLog >= 0)
590 {
591// printf ("Tmp file: %s\n", tmpfile);
592 unlink (tmpfile); // make sure the file will be deleted on close
593 }
594#endif
595 if (myFDLog < 0)
596 {
597 perror ("Error creating temporary file for capturing console output");
598 printf ("path: %s\n", tmpfile);
599 return;
600 }
601 }
602
aa02980d 603 myDoLog = doLog;
604}
605
606void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
607{
608 myDoEcho = doEcho;
609}
610
611Standard_Boolean Draw_Interpretor::GetDoLog () const
612{
613 return myDoLog;
614}
615
616Standard_Boolean Draw_Interpretor::GetDoEcho () const
617{
618 return myDoEcho;
619}
620
e05c25c1 621void Draw_Interpretor::ResetLog ()
aa02980d 622{
e05c25c1 623 if (myFDLog < 0)
624 return;
625
626 // flush cerr and cout, for the case if they are bound to the log
627 flush_standard_streams();
628
629 lseek (myFDLog, 0, SEEK_SET);
630
631#ifdef _WIN32
632 if (_chsize_s (myFDLog, 0) != 0)
633#else
634 if (ftruncate (myFDLog, 0) != 0)
635#endif
636 {
637 perror ("Error truncating the console log");
638 }
639}
640
641void Draw_Interpretor::AddLog (const Standard_CString theStr)
642{
643 if (myFDLog < 0 || ! theStr || ! theStr[0])
644 return;
645
646 // flush cerr and cout, for the case if they are bound to the log
647 flush_standard_streams();
648
649 // write as plain bytes
650 if (write (myFDLog, theStr, (unsigned int)strlen(theStr)) <0)
651 {
652 perror ("Error writing to console log");
653 }
654}
655
656TCollection_AsciiString Draw_Interpretor::GetLog ()
657{
658 TCollection_AsciiString aLog;
659 if (myFDLog < 0)
660 return aLog;
661
662 // flush cerr and cout
663 flush_standard_streams();
664
665 // rewind the file to its start
666 lseek (myFDLog, 0, SEEK_SET);
667
668 // read the whole log to string; this implementation
669 // is not optimized but should be sufficient
670 const int BUFSIZE = 4096;
671 char buffer[BUFSIZE + 1];
672 for (;;)
673 {
674 int nbRead = read (myFDLog, buffer, BUFSIZE);
675 if (nbRead <= 0)
676 {
677 break;
678 }
679 buffer[nbRead] = '\0';
680 aLog.AssignCat (buffer);
681 }
682
683 return aLog;
60be1f9b 684}