68fb8f9e792e5d972722e19acef974d671e806f7
[occt.git] / src / Draw / Draw_Interpretor.cxx
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
5 //
6 // This file is part of Open CASCADE Technology software library.
7 //
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.
13 //
14 // Alternatively, this file may be used under the terms of Open CASCADE
15 // commercial license or contractual agreement.
16
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>
23
24 #include <TCollection_AsciiString.hxx>
25 #include <TCollection_ExtendedString.hxx>
26 #include <OSD_Process.hxx>
27 #include <OSD_Path.hxx>
28 #include <OSD.hxx>
29 #include <OSD_File.hxx>
30
31 #include <string.h>
32 #include <tcl.h>
33 #ifndef _WIN32
34 #include <unistd.h>
35 #endif
36
37 // for capturing of cout and cerr (dup(), dup2())
38 #ifdef _WIN32
39 #include <io.h>
40 #endif
41
42 #if ! defined(STDOUT_FILENO)
43 #define STDOUT_FILENO fileno(stdout)
44 #endif
45 #if ! defined(STDERR_FILENO)
46 #define STDERR_FILENO fileno(stderr)
47 #endif
48
49 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)))
50 #define TCL_USES_UTF8
51 #endif
52
53 // logging helpers
54 namespace {
55   void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
56   {
57     for (int i=0; i < argc; i++)
58       os << argv[i] << " ";
59     os << endl;
60   }
61
62   void flush_standard_streams ()
63   {
64     fflush (stderr);
65     fflush (stdout);
66     cerr << flush;
67     cout << flush;
68   }
69
70   int capture_start (OSD_File& theTmpFile, int std_fd)
71   {
72     theTmpFile.BuildTemporary();
73     if (theTmpFile.Failed())
74     {
75       cerr << "Error: cannot create temporary file for capturing console output" << endl;
76       return -1;
77     }
78
79     // remember current file descriptors of standard stream, and replace it by temporary
80     return theTmpFile.Capture(std_fd);
81   }
82
83   void capture_end (OSD_File* tmp_file, int std_fd, int save_fd, Standard_OStream &log, Standard_Boolean doEcho)
84   {
85     if (!tmp_file)
86       return;
87
88     // restore normal descriptors of console stream
89   #ifdef _WIN32
90     _dup2(save_fd, std_fd);
91     _close(save_fd);
92   #else
93     dup2(save_fd, std_fd);
94     close(save_fd);
95   #endif
96
97     // extract all output and copy it to log and optionally to cout
98     const int BUFSIZE = 2048;
99     TCollection_AsciiString buf;
100     tmp_file->Rewind();
101     while (tmp_file->ReadLine (buf, BUFSIZE) > 0)
102     {
103       log << buf;
104       if (doEcho) 
105         cout << buf;
106     }
107
108     // close temporary file
109     tmp_file->Close();
110
111     // remove temporary file if this is not done by the system
112     if (tmp_file->Exists())
113       tmp_file->Remove();
114   }
115
116 } // anonymous namespace
117
118 // MKV 29.03.05
119 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
120 static Standard_Integer CommandCmd 
121 (ClientData theClientData, Tcl_Interp *interp,
122  Standard_Integer argc, const char* argv[])
123 #else
124 static Standard_Integer CommandCmd 
125 (ClientData theClientData, Tcl_Interp *interp,
126  Standard_Integer argc, char* argv[])
127 #endif
128 {
129   static Standard_Integer code;
130   code = TCL_OK;
131   Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
132   Draw_Interpretor& di = *(aCallback->myDI);
133
134   // log command execution, except commands manipulating log itself and echo
135   Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 || 
136                                         strcmp (argv[0], "decho") == 0);
137   Standard_Boolean doLog  = (di.GetDoLog() && ! isLogManipulation);
138   Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
139   if (doLog)
140     dumpArgs (di.Log(), argc, argv);
141   if (doEcho)
142     dumpArgs (cout, argc, argv);
143
144   // flush cerr and cout
145   flush_standard_streams();
146
147   // capture cout and cerr to log
148   OSD_File aFile_out, aFile_err;
149   int fd_err_save = -1;
150   int fd_out_save = -1;
151   if (doLog)
152   {
153     fd_out_save = capture_start (aFile_out, STDOUT_FILENO);
154     fd_err_save = capture_start (aFile_err, STDERR_FILENO);
155   }
156
157   // run command
158   try {
159     OCC_CATCH_SIGNALS
160
161     // get exception if control-break has been pressed 
162     OSD::ControlBreak();
163
164     // OCC680: Transfer UTF-8 directly to OCC commands without locale usage
165       
166     Standard_Integer fres = aCallback->Invoke ( di, argc, argv /*anArgs.GetArgv()*/ );
167     if (fres != 0) 
168       code = TCL_ERROR;
169   }
170   catch (Standard_Failure) {
171
172     Handle(Standard_Failure) E = Standard_Failure::Caught();
173
174     // fail if Draw_ExitOnCatch is set
175     // MKV 29.03.05
176 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
177     const char*  cc = Tcl_GetVar(interp,
178                           "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
179 #else
180     char* const cc = Tcl_GetVar(interp,
181                           "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
182 #endif
183
184     cout << "An exception was caught " << E << endl;
185
186     if (cc && Draw::Atoi(cc)) {
187 #ifdef _WIN32
188       Tcl_Exit(0);
189 #else      
190       Tcl_Eval(interp,"exit");
191 #endif
192     }
193
194     // get the error message
195     Standard_SStream ss;
196     ss << "** Exception ** " << E << ends;
197     Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
198     code = TCL_ERROR;
199   }
200
201   // flush streams
202   flush_standard_streams();
203
204   // end capturing cout and cerr 
205   if (doLog) 
206   {
207     capture_end (&aFile_err, STDERR_FILENO, fd_err_save, di.Log(), doEcho);
208     capture_end (&aFile_out, STDOUT_FILENO, fd_out_save, di.Log(), doEcho);
209   }
210
211   // log command result
212   const char* aResultStr = NULL;
213   if (doLog)
214   {
215     aResultStr = Tcl_GetStringResult (interp);
216     if (aResultStr != 0 && aResultStr[0] != '\0' )
217       di.Log() << Tcl_GetStringResult (interp) << endl;
218   }
219   if (doEcho)
220   {
221     if (aResultStr == NULL)
222       aResultStr = Tcl_GetStringResult (interp);
223     if (aResultStr != 0 && aResultStr[0] != '\0' )
224       cout << Tcl_GetStringResult (interp) << endl;
225   }
226
227   return code;
228 }
229
230 static void CommandDelete (ClientData theClientData)
231 {
232   Draw_Interpretor::CallBackData* aCallback = (Draw_Interpretor::CallBackData* )theClientData;
233   delete aCallback;
234 }
235
236 //=======================================================================
237 //function : Draw_Interpretor
238 //purpose  : 
239 //=======================================================================
240
241 Draw_Interpretor::Draw_Interpretor() :
242   isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False)
243 {
244 // The tcl interpreter is not created immediately as it is kept 
245 // by a global variable and created and deleted before the main().
246   myInterp  = NULL;
247 }
248
249 //=======================================================================
250 //function : Init
251 //purpose  : It is necessary to call this function
252 //=======================================================================
253
254 void Draw_Interpretor::Init()
255 {
256   if (isAllocated) 
257     Tcl_DeleteInterp(myInterp);
258   isAllocated=Standard_True;
259   myInterp=Tcl_CreateInterp();
260 }
261
262 //=======================================================================
263 //function : Draw_Interpretor
264 //purpose  : 
265 //=======================================================================
266
267 Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
268   isAllocated(Standard_False),
269   myInterp(p),
270   myDoLog(Standard_False),
271   myDoEcho(Standard_False)
272 {
273 }
274
275 //=======================================================================
276 //function : add
277 //purpose  :
278 //=======================================================================
279 void Draw_Interpretor::add (const Standard_CString          theCommandName,
280                             const Standard_CString          theHelp,
281                             const Standard_CString          theFileName,
282                             Draw_Interpretor::CallBackData* theCallback,
283                             const Standard_CString          theGroup)
284 {
285   if (myInterp == NULL)
286   {
287     Init();
288   }
289
290   Standard_PCharacter aName  = (Standard_PCharacter )theCommandName;
291   Standard_PCharacter aHelp  = (Standard_PCharacter )theHelp;
292   Standard_PCharacter aGroup = (Standard_PCharacter )theGroup;
293   Tcl_CreateCommand (myInterp, aName, CommandCmd, (ClientData )theCallback, CommandDelete);
294
295   // add the help
296   Tcl_SetVar2 (myInterp, "Draw_Helps",  aName,  aHelp, TCL_GLOBAL_ONLY);
297   Tcl_SetVar2 (myInterp, "Draw_Groups", aGroup, aName,
298                      TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
299
300   // add path to source file (keep not more than two last subdirectories)
301   if (theFileName  == NULL
302    || *theFileName == '\0')
303   {
304     return;
305   }
306
307   OSD_Path aPath (theFileName);
308   Standard_Integer nbTrek = aPath.TrekLength();
309   for (Standard_Integer i = 2; i < nbTrek; ++i)
310   {
311     aPath.RemoveATrek (1);
312   }
313   aPath.SetDisk ("");
314   aPath.SetNode ("");
315   TCollection_AsciiString aSrcPath;
316   aPath.SystemName (aSrcPath);
317   Tcl_SetVar2 (myInterp, "Draw_Files", aName, aSrcPath.ToCString(), TCL_GLOBAL_ONLY);
318 }
319
320 //=======================================================================
321 //function : Remove
322 //purpose  : 
323 //=======================================================================
324
325 Standard_Boolean Draw_Interpretor::Remove(Standard_CString const n)
326 {
327   Standard_PCharacter pN;
328   //
329   pN=(Standard_PCharacter)n;
330  
331   Standard_Integer result = Tcl_DeleteCommand(myInterp,pN);
332   return result == 0;
333 }
334
335 //=======================================================================
336 //function : Result
337 //purpose  : 
338 //=======================================================================
339
340 Standard_CString Draw_Interpretor::Result() const
341 {
342 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
343   return Tcl_GetStringResult(myInterp);
344 #else
345   return myInterp->result;
346 #endif
347 }
348
349 //=======================================================================
350 //function : Reset
351 //purpose  : 
352 //=======================================================================
353
354 void Draw_Interpretor::Reset()
355 {
356   Tcl_ResetResult(myInterp);
357 }
358
359 //=======================================================================
360 //function : Append
361 //purpose  : 
362 //=======================================================================
363
364 Draw_Interpretor& Draw_Interpretor::Append(const Standard_CString s)
365 {
366 #ifdef TCL_USES_UTF8
367   // Convert string to UTF-8 format for Tcl
368   Tcl_DString TclString;
369   Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
370   Tcl_AppendResult ( myInterp, Tcl_DStringValue ( &TclString ), (Standard_CString)0 );
371   Tcl_DStringFree ( &TclString );
372 #else
373   Tcl_AppendResult(myInterp,s,(Standard_CString)0);
374 #endif
375   return *this;
376 }
377
378 //=======================================================================
379 //function : Append
380 //purpose  : 
381 //=======================================================================
382
383 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_AsciiString& s)
384 {
385   return Append (s.ToCString());
386 }
387
388 //=======================================================================
389 //function : Append
390 //purpose  : 
391 //=======================================================================
392
393 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_ExtendedString& theString)
394 {
395 #ifdef TCL_USES_UTF8
396   // Convert string to UTF-8 format for Tcl
397   char *str = new char[theString.LengthOfCString()+1];
398   theString.ToUTF8CString (str);
399   Tcl_AppendResult ( myInterp, str, (Standard_CString)0 );
400   delete[] str;
401 #else
402   // put as ascii string, replacing non-ascii characters by '?'
403   TCollection_AsciiString str (theString, '?');
404   Tcl_AppendResult(myInterp,str.ToCString(),(Standard_CString)0);
405 #endif
406   return *this;
407 }
408
409 //=======================================================================
410 //function : Append
411 //purpose  : 
412 //=======================================================================
413
414 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Integer i)
415 {
416   char c[100];
417   Sprintf(c,"%d",i);
418   Tcl_AppendResult(myInterp,c,(Standard_CString)0);
419   return *this;
420 }
421
422 //=======================================================================
423 //function : Append
424 //purpose  : 
425 //=======================================================================
426
427 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
428 {
429   char s[100];
430   Sprintf(s,"%.17g",r);
431   Tcl_AppendResult(myInterp,s,(Standard_CString)0);
432   return *this;
433 }
434
435 //=======================================================================
436 //function : Append
437 //purpose  : 
438 //=======================================================================
439
440 Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
441 {
442   return Append (s.str().c_str());
443 }
444
445 //=======================================================================
446 //function : AppendElement
447 //purpose  : 
448 //=======================================================================
449
450 void Draw_Interpretor::AppendElement(const Standard_CString s)
451 {
452 #ifdef TCL_USES_UTF8
453   // Convert string to UTF-8 format for Tcl
454   Tcl_DString TclString;
455   Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
456   Tcl_AppendElement ( myInterp, Tcl_DStringValue ( &TclString ) );
457   Tcl_DStringFree ( &TclString );
458 #else
459 #ifdef IRIX
460   //AppendElement is declared as (Tcl_Interp *interp, char *string)
461   //on SGI 32
462   Tcl_AppendElement(myInterp,(char*) s);
463 #else
464   Tcl_AppendElement(myInterp, s);
465 #endif
466 #endif
467 }
468
469 //=======================================================================
470 //function : Eval
471 //purpose  : 
472 //=======================================================================
473
474 Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
475 {
476   Standard_PCharacter pLine;
477   //
478   pLine=(Standard_PCharacter)line;
479   //
480   return Tcl_Eval(myInterp,pLine);
481 }
482
483
484 //=======================================================================
485 //function : Eval
486 //purpose  : 
487 //=======================================================================
488
489 Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
490                                                  const Standard_Integer flags)
491 {
492   Standard_PCharacter pLine;
493   //
494   pLine=(Standard_PCharacter)line;
495   return Tcl_RecordAndEval(myInterp,pLine,flags);
496 }
497
498 //=======================================================================
499 //function : EvalFile
500 //purpose  : 
501 //=======================================================================
502
503 Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
504 {
505   Standard_PCharacter pfname;
506   //
507   pfname=(Standard_PCharacter)fname;
508   return Tcl_EvalFile(myInterp,pfname);
509 }
510
511 //=======================================================================
512 //function : PrintHelp
513 //purpose  :
514 //=======================================================================
515
516 Standard_Integer Draw_Interpretor::PrintHelp (const Standard_CString theCommandName)
517 {
518   TCollection_AsciiString aCmd     = TCollection_AsciiString ("help ") + theCommandName;
519   Standard_PCharacter     aLinePtr = (Standard_PCharacter )aCmd.ToCString();
520   return Tcl_Eval (myInterp, aLinePtr);
521 }
522
523 //=======================================================================
524 //function :Complete
525 //purpose  : 
526 //=======================================================================
527
528 Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
529 {
530   Standard_PCharacter pLine;
531   //
532   pLine=(Standard_PCharacter)line;
533   return Tcl_CommandComplete (pLine) != 0;
534 }
535
536 //=======================================================================
537 //function : Destroy
538 //purpose  : 
539 //=======================================================================
540
541 Draw_Interpretor::~Draw_Interpretor()
542 {
543   // MKV 01.02.05
544 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
545   try {
546     OCC_CATCH_SIGNALS
547     Tcl_Exit(0);
548   }
549   catch (Standard_Failure) {
550 #ifdef OCCT_DEBUG
551     cout <<"Tcl_Exit have an exeption" << endl;
552 #endif
553   }
554 #else
555 #ifdef _WIN32
556   Tcl_Exit(0);
557 #endif  
558 #endif
559 }
560
561 //=======================================================================
562 //function : Interp
563 //purpose  : 
564 //=======================================================================
565
566 Draw_PInterp Draw_Interpretor::Interp() const
567 {
568   Standard_DomainError_Raise_if (myInterp==NULL , "No call for  Draw_Interpretor::Init()");
569   return myInterp;
570 }
571
572 void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
573 {
574   if (isAllocated)
575     Tcl_DeleteInterp(myInterp);
576   isAllocated = Standard_False;
577   myInterp = PIntrp;
578 }
579
580 //=======================================================================
581 //function : Logging
582 //purpose  : 
583 //=======================================================================
584
585 void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
586 {
587   myDoLog = doLog;
588 }
589
590 void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
591 {
592   myDoEcho = doEcho;
593 }
594
595 Standard_Boolean Draw_Interpretor::GetDoLog () const
596 {
597   return myDoLog;
598 }
599
600 Standard_Boolean Draw_Interpretor::GetDoEcho () const
601 {
602   return myDoEcho;
603 }
604
605 Standard_SStream& Draw_Interpretor::Log ()
606 {
607   return myLog;
608 }