0030579: Draw Harness, Draw_Interpretor - catch exceptions other than Standard_Failure
[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
f996b507 115static Standard_Integer CommandCmd (ClientData theClientData, Tcl_Interp* interp, Standard_Integer argc, const char* argv[])
7fd59977 116{
117 static Standard_Integer code;
118 code = TCL_OK;
dda67c1c 119 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
120 Draw_Interpretor& di = *(aCallback->myDI);
aa02980d 121
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);
aa02980d 127
128 // flush cerr and cout
129 flush_standard_streams();
130
131 // capture cout and cerr to log
e05c25c1 132 int aFDstdout = STDOUT_FILENO;
133 int aFDstderr = STDERR_FILENO;
134 int aFDerr_save = -1;
135 int aFDout_save = -1;
aa02980d 136 if (doLog)
137 {
e05c25c1 138 aFDout_save = capture_start (aFDstdout, di.GetLogFileDescriptor());
139 aFDerr_save = capture_start (aFDstderr, di.GetLogFileDescriptor());
aa02980d 140 }
7fd59977 141
e05c25c1 142 if (doEcho || doLog)
143 dumpArgs (cout, argc, argv);
144
aa02980d 145 // run command
7fd59977 146 try {
147 OCC_CATCH_SIGNALS
148
8a262fa1 149 // get exception if control-break has been pressed
150 OSD::ControlBreak();
151
d9ff84e8 152 // OCC680: Transfer UTF-8 directly to OCC commands without locale usage
7fd59977 153
d9ff84e8 154 Standard_Integer fres = aCallback->Invoke ( di, argc, argv /*anArgs.GetArgv()*/ );
7fd59977 155 if (fres != 0)
156 code = TCL_ERROR;
157 }
9775fa61 158 catch (Standard_Failure const& anException) {
7fd59977 159 // fail if Draw_ExitOnCatch is set
f996b507 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))
163 {
57c28b61 164#ifdef _WIN32
7fd59977 165 Tcl_Exit(0);
166#else
167 Tcl_Eval(interp,"exit");
168#endif
169 }
170
171 // get the error message
172 Standard_SStream ss;
9775fa61 173 ss << "** Exception ** " << anException << ends;
7fd59977 174 Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
7fd59977 175 code = TCL_ERROR;
176 }
f996b507 177 catch (std::exception const& theStdException)
178 {
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))
182 {
183 #ifdef _WIN32
184 Tcl_Exit (0);
185 #else
186 Tcl_Eval (interp, "exit");
187 #endif
188 }
189
190 // get the error message
191 Standard_SStream ss;
192 ss << "** Exception ** " << theStdException.what() << " [" << typeid(theStdException).name() << "]" << ends;
193 Tcl_SetResult (interp, (char*)(ss.str().c_str()), TCL_VOLATILE);
194 code = TCL_ERROR;
195 }
196 catch (...)
197 {
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))
201 {
202 #ifdef _WIN32
203 Tcl_Exit (0);
204 #else
205 Tcl_Eval (interp,"exit");
206 #endif
207 }
208
209 // get the error message
210 Standard_SStream ss;
211 ss << "** Exception ** UNKNOWN" << ends;
212 Tcl_SetResult (interp, (char* )(ss.str().c_str()), TCL_VOLATILE);
213 code = TCL_ERROR;
214 }
aa02980d 215
e05c25c1 216 // log command result
217 if (doLog || doEcho)
218 {
219 const char* aResultStr = Tcl_GetStringResult (interp);
220 if (aResultStr != 0 && aResultStr[0] != '\0' )
221 {
222 std::cout << aResultStr << std::endl;
223 }
224 }
225
aa02980d 226 // flush streams
227 flush_standard_streams();
228
229 // end capturing cout and cerr
230 if (doLog)
231 {
e05c25c1 232 capture_end (aFDstderr, aFDerr_save);
233 capture_end (aFDstdout, aFDout_save);
aa02980d 234 }
235
7fd59977 236 return code;
237}
238
dda67c1c 239static void CommandDelete (ClientData theClientData)
7fd59977 240{
dda67c1c 241 Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
242 delete aCallback;
7fd59977 243}
244
245//=======================================================================
246//function : Draw_Interpretor
247//purpose :
248//=======================================================================
249
250Draw_Interpretor::Draw_Interpretor() :
e05c25c1 251 isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False), myFDLog(-1)
7fd59977 252{
0d969553
Y
253// The tcl interpreter is not created immediately as it is kept
254// by a global variable and created and deleted before the main().
7fd59977 255 myInterp = NULL;
256}
257
258//=======================================================================
259//function : Init
0d969553 260//purpose : It is necessary to call this function
7fd59977 261//=======================================================================
262
263void Draw_Interpretor::Init()
264{
265 if (isAllocated)
266 Tcl_DeleteInterp(myInterp);
267 isAllocated=Standard_True;
268 myInterp=Tcl_CreateInterp();
269}
270
271//=======================================================================
272//function : Draw_Interpretor
273//purpose :
274//=======================================================================
275
276Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
277 isAllocated(Standard_False),
aa02980d 278 myInterp(p),
279 myDoLog(Standard_False),
280 myDoEcho(Standard_False)
7fd59977 281{
282}
283
284//=======================================================================
dda67c1c 285//function : add
286//purpose :
7fd59977 287//=======================================================================
dda67c1c 288void 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)
7fd59977 293{
8de8dacd 294 Standard_ASSERT_RAISE (myInterp != NULL, "Attempt to add command to Null interpretor");
7fd59977 295
dda67c1c 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);
7fd59977 300
301 // add the help
dda67c1c 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);
938a360f 305
d33dea30 306 // add path to source file (keep not more than two last subdirectories)
dda67c1c 307 if (theFileName == NULL
308 || *theFileName == '\0')
309 {
310 return;
311 }
312
313 OSD_Path aPath (theFileName);
d33dea30 314 Standard_Integer nbTrek = aPath.TrekLength();
dda67c1c 315 for (Standard_Integer i = 2; i < nbTrek; ++i)
316 {
d33dea30 317 aPath.RemoveATrek (1);
dda67c1c 318 }
319 aPath.SetDisk ("");
320 aPath.SetNode ("");
d33dea30
PK
321 TCollection_AsciiString aSrcPath;
322 aPath.SystemName (aSrcPath);
36cc2619 323 if (aSrcPath.Value(1) == '/')
324 aSrcPath.Remove(1);
dda67c1c 325 Tcl_SetVar2 (myInterp, "Draw_Files", aName, aSrcPath.ToCString(), TCL_GLOBAL_ONLY);
7fd59977 326}
327
7fd59977 328//=======================================================================
329//function : Remove
330//purpose :
331//=======================================================================
332
333Standard_Boolean Draw_Interpretor::Remove(Standard_CString const n)
334{
335 Standard_PCharacter pN;
336 //
337 pN=(Standard_PCharacter)n;
338
339 Standard_Integer result = Tcl_DeleteCommand(myInterp,pN);
340 return result == 0;
341}
342
343//=======================================================================
344//function : Result
345//purpose :
346//=======================================================================
347
348Standard_CString Draw_Interpretor::Result() const
349{
350#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
351 return Tcl_GetStringResult(myInterp);
352#else
353 return myInterp->result;
354#endif
355}
356
357//=======================================================================
358//function : Reset
359//purpose :
360//=======================================================================
361
362void Draw_Interpretor::Reset()
363{
364 Tcl_ResetResult(myInterp);
365}
366
367//=======================================================================
368//function : Append
369//purpose :
370//=======================================================================
371
372Draw_Interpretor& Draw_Interpretor::Append(const Standard_CString s)
373{
374#ifdef TCL_USES_UTF8
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 );
380#else
381 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
382#endif
383 return *this;
384}
385
386//=======================================================================
387//function : Append
388//purpose :
389//=======================================================================
390
391Draw_Interpretor& Draw_Interpretor::Append(const TCollection_AsciiString& s)
392{
393 return Append (s.ToCString());
394}
395
396//=======================================================================
397//function : Append
398//purpose :
399//=======================================================================
400
401Draw_Interpretor& Draw_Interpretor::Append(const TCollection_ExtendedString& theString)
402{
403#ifdef TCL_USES_UTF8
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 );
408 delete[] str;
409#else
410 // put as ascii string, replacing non-ascii characters by '?'
411 TCollection_AsciiString str (theString, '?');
412 Tcl_AppendResult(myInterp,str.ToCString(),(Standard_CString)0);
413#endif
414 return *this;
415}
416
417//=======================================================================
418//function : Append
419//purpose :
420//=======================================================================
421
422Draw_Interpretor& Draw_Interpretor::Append(const Standard_Integer i)
423{
424 char c[100];
91322f44 425 Sprintf(c,"%d",i);
7fd59977 426 Tcl_AppendResult(myInterp,c,(Standard_CString)0);
427 return *this;
428}
429
430//=======================================================================
431//function : Append
432//purpose :
433//=======================================================================
434
435Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
436{
437 char s[100];
91322f44 438 Sprintf(s,"%.17g",r);
7fd59977 439 Tcl_AppendResult(myInterp,s,(Standard_CString)0);
440 return *this;
441}
442
443//=======================================================================
444//function : Append
445//purpose :
446//=======================================================================
447
448Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
449{
7fd59977 450 return Append (s.str().c_str());
7fd59977 451}
452
453//=======================================================================
454//function : AppendElement
455//purpose :
456//=======================================================================
457
458void Draw_Interpretor::AppendElement(const Standard_CString s)
459{
460#ifdef TCL_USES_UTF8
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 );
466#else
467#ifdef IRIX
468 //AppendElement is declared as (Tcl_Interp *interp, char *string)
469 //on SGI 32
470 Tcl_AppendElement(myInterp,(char*) s);
471#else
472 Tcl_AppendElement(myInterp, s);
473#endif
474#endif
475}
476
477//=======================================================================
478//function : Eval
479//purpose :
480//=======================================================================
481
482Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
483{
a2f76b15 484 return Tcl_Eval(myInterp,line);
7fd59977 485}
486
487
488//=======================================================================
489//function : Eval
490//purpose :
491//=======================================================================
492
493Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
494 const Standard_Integer flags)
495{
a2f76b15 496 return Tcl_RecordAndEval(myInterp,line,flags);
7fd59977 497}
498
499//=======================================================================
500//function : EvalFile
501//purpose :
502//=======================================================================
503
504Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
505{
a2f76b15 506 return Tcl_EvalFile(myInterp,fname);
7fd59977 507}
508
785a9540 509//=======================================================================
510//function : PrintHelp
511//purpose :
512//=======================================================================
513
514Standard_Integer Draw_Interpretor::PrintHelp (const Standard_CString theCommandName)
515{
516 TCollection_AsciiString aCmd = TCollection_AsciiString ("help ") + theCommandName;
517 Standard_PCharacter aLinePtr = (Standard_PCharacter )aCmd.ToCString();
518 return Tcl_Eval (myInterp, aLinePtr);
519}
520
7fd59977 521//=======================================================================
522//function :Complete
523//purpose :
524//=======================================================================
525
526Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
527{
528 Standard_PCharacter pLine;
529 //
530 pLine=(Standard_PCharacter)line;
dde68833 531 return Tcl_CommandComplete (pLine) != 0;
7fd59977 532}
533
534//=======================================================================
535//function : Destroy
536//purpose :
537//=======================================================================
538
dda67c1c 539Draw_Interpretor::~Draw_Interpretor()
7fd59977 540{
e05c25c1 541 SetDoLog (Standard_False);
542 if (myFDLog >=0)
543 {
544 close (myFDLog);
545 myFDLog = 0;
546 }
547
7fd59977 548 // MKV 01.02.05
549#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
550 try {
551 OCC_CATCH_SIGNALS
552 Tcl_Exit(0);
553 }
554 catch (Standard_Failure) {
0797d9d3 555#ifdef OCCT_DEBUG
7fd59977 556 cout <<"Tcl_Exit have an exeption" << endl;
557#endif
558 }
559#else
57c28b61 560#ifdef _WIN32
7fd59977 561 Tcl_Exit(0);
562#endif
563#endif
564}
565
566//=======================================================================
567//function : Interp
568//purpose :
569//=======================================================================
570
571Draw_PInterp Draw_Interpretor::Interp() const
572{
573 Standard_DomainError_Raise_if (myInterp==NULL , "No call for Draw_Interpretor::Init()");
574 return myInterp;
575}
576
577void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
578{
579 if (isAllocated)
580 Tcl_DeleteInterp(myInterp);
581 isAllocated = Standard_False;
582 myInterp = PIntrp;
583}
aa02980d 584
585//=======================================================================
586//function : Logging
587//purpose :
588//=======================================================================
589
590void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
591{
e05c25c1 592 if (myDoLog == doLog)
593 return;
594
595 // create log file if not opened yet
596 if (doLog && myFDLog < 0)
597 {
598#ifdef _WIN32
599 char tmpfile[L_tmpnam + 1];
600 tmpnam(tmpfile);
601 myFDLog = open (tmpfile, O_RDWR | O_CREAT | O_EXCL | O_TEMPORARY, S_IREAD | S_IWRITE);
602#else
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);
607 if (myFDLog >= 0)
608 {
609// printf ("Tmp file: %s\n", tmpfile);
610 unlink (tmpfile); // make sure the file will be deleted on close
611 }
612#endif
613 if (myFDLog < 0)
614 {
615 perror ("Error creating temporary file for capturing console output");
616 printf ("path: %s\n", tmpfile);
617 return;
618 }
619 }
620
aa02980d 621 myDoLog = doLog;
622}
623
624void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
625{
626 myDoEcho = doEcho;
627}
628
629Standard_Boolean Draw_Interpretor::GetDoLog () const
630{
631 return myDoLog;
632}
633
634Standard_Boolean Draw_Interpretor::GetDoEcho () const
635{
636 return myDoEcho;
637}
638
e05c25c1 639void Draw_Interpretor::ResetLog ()
aa02980d 640{
e05c25c1 641 if (myFDLog < 0)
642 return;
643
644 // flush cerr and cout, for the case if they are bound to the log
645 flush_standard_streams();
646
647 lseek (myFDLog, 0, SEEK_SET);
648
649#ifdef _WIN32
650 if (_chsize_s (myFDLog, 0) != 0)
651#else
652 if (ftruncate (myFDLog, 0) != 0)
653#endif
654 {
655 perror ("Error truncating the console log");
656 }
657}
658
659void Draw_Interpretor::AddLog (const Standard_CString theStr)
660{
661 if (myFDLog < 0 || ! theStr || ! theStr[0])
662 return;
663
664 // flush cerr and cout, for the case if they are bound to the log
665 flush_standard_streams();
666
667 // write as plain bytes
668 if (write (myFDLog, theStr, (unsigned int)strlen(theStr)) <0)
669 {
670 perror ("Error writing to console log");
671 }
672}
673
674TCollection_AsciiString Draw_Interpretor::GetLog ()
675{
676 TCollection_AsciiString aLog;
677 if (myFDLog < 0)
678 return aLog;
679
680 // flush cerr and cout
681 flush_standard_streams();
682
683 // rewind the file to its start
684 lseek (myFDLog, 0, SEEK_SET);
685
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];
690 for (;;)
691 {
692 int nbRead = read (myFDLog, buffer, BUFSIZE);
693 if (nbRead <= 0)
694 {
695 break;
696 }
697 buffer[nbRead] = '\0';
698 aLog.AssignCat (buffer);
699 }
700
701 return aLog;
60be1f9b 702}