7a9aac764e32ba9645e81964d2582f085d0cfb3d
[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-2012 OPEN CASCADE SAS
5 //
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.
10 //
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.
13 //
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.
20
21
22
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>
29
30 #include <TCollection_AsciiString.hxx>
31 #include <TCollection_ExtendedString.hxx>
32 #include <OSD_Path.hxx>
33 #include <OSD.hxx>
34
35 #include <string.h>
36 #include <tcl.h>
37
38 // for capturing of cout and cerr (dup(), dup2())
39 #ifdef _MSC_VER
40 #include <io.h>
41 #endif
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46 #if ! defined(STDOUT_FILENO)
47 #define STDOUT_FILENO fileno(stdout)
48 #endif
49 #if ! defined(STDERR_FILENO)
50 #define STDERR_FILENO fileno(stderr)
51 #endif
52
53 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)))
54 #define TCL_USES_UTF8
55 #endif
56
57 //
58 // Auxiliary tool to convert strings in command arguments from UTF-8 
59 // (Tcl internal encoding since Tcl 8.1) to system local encoding, 
60 // normally extended Ascii as expected by OCC commands
61 //
62 class TclUTFToLocalStringSentry {
63  public:
64
65 #ifdef TCL_USES_UTF8
66   TclUTFToLocalStringSentry (int argc, const char **argv) :
67     nb(0),
68     TclArgv(new Tcl_DString[argc]),
69     Argv(new char*[argc])
70   {
71     for (; nb < argc; nb++ ) {
72       Tcl_UtfToExternalDString ( NULL, argv[nb], -1, &TclArgv[nb] );
73       Argv[nb] = Tcl_DStringValue ( &TclArgv[nb] );
74     }
75   }
76   
77   ~TclUTFToLocalStringSentry () 
78   {
79     delete[] Argv;
80     while ( nb-- >0 ) Tcl_DStringFree ( &TclArgv[nb] );
81     delete[] TclArgv;
82   }
83 #else
84   TclUTFToLocalStringSentry (int, const char **argv) : Argv((char**)argv) {}
85 #endif
86
87   const char **GetArgv () const { return (const char **)Argv; }
88   
89  private:
90   int nb;
91   Tcl_DString *TclArgv;
92   char **Argv;
93 };
94
95
96 //
97 // Call backs for TCL
98 //
99
100 struct CData {
101   CData(Draw_CommandFunction ff, Draw_Interpretor* ii) : f(ff), i(ii) {}
102   Draw_CommandFunction f;
103   Draw_Interpretor*    i;
104 };
105
106 // logging helpers
107 namespace {
108   void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
109   {
110     for (int i=0; i < argc; i++)
111       os << argv[i] << " ";
112     os << endl;
113   }
114
115   void flush_standard_streams ()
116   {
117     fflush (stderr);
118     fflush (stdout);
119     cerr << flush;
120     cout << flush;
121   }
122
123   FILE* capture_start (int std_fd, int *save_fd)
124   {
125     (*save_fd) = 0;
126
127     // open temporary files
128     FILE * aTmpFile = tmpfile();
129     int fd_tmp = fileno(aTmpFile);
130
131     if (fd_tmp <0) 
132     {
133       cerr << "Error: cannot create temporary file for capturing console output" << endl;
134       fclose (aTmpFile);
135       return NULL;
136     }
137
138     // remember current file descriptors of standard stream, and replace it by temporary
139     (*save_fd) = dup(std_fd);
140     dup2(fd_tmp, std_fd);
141     return aTmpFile;
142   }
143
144   void capture_end (FILE* tmp_file, int std_fd, int save_fd, Standard_OStream &log, Standard_Boolean doEcho)
145   {
146     // restore normal descriptors of console stream
147     dup2 (save_fd, std_fd);
148     close(save_fd);
149
150     // extract all output and copy it to log and optionally to cout
151     const int BUFSIZE = 2048;
152     char buf[BUFSIZE];
153     rewind(tmp_file);
154     while (fgets (buf, BUFSIZE, tmp_file) != NULL)
155     {
156       log << buf;
157       if (doEcho) 
158         cout << buf;
159     }
160
161     // close temporary file
162     fclose (tmp_file);
163   }
164 };
165
166 // MKV 29.03.05
167 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
168 static Standard_Integer CommandCmd 
169 (ClientData clientData, Tcl_Interp *interp,
170  Standard_Integer argc, const char* argv[])
171 #else
172 static Standard_Integer CommandCmd 
173 (ClientData clientData, Tcl_Interp *interp,
174  Standard_Integer argc, char* argv[])
175 #endif
176 {
177   static Standard_Integer code;
178   code = TCL_OK;
179   CData* C = (CData*) clientData;
180   Draw_Interpretor& di = *(C->i);
181
182   // log command execution, except commands manipulating log itself and echo
183   Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 || 
184                                         strcmp (argv[0], "decho") == 0);
185   Standard_Boolean doLog  = (di.GetDoLog() && ! isLogManipulation);
186   Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
187   if (doLog)
188     dumpArgs (di.Log(), argc, argv);
189   if (doEcho)
190     dumpArgs (cout, argc, argv);
191
192   // flush cerr and cout
193   flush_standard_streams();
194
195   // capture cout and cerr to log
196   FILE * aFile_err = NULL;
197   FILE * aFile_out = NULL;
198   int fd_err_save = 0;
199   int fd_out_save = 0;
200   if (doLog)
201   {
202     aFile_out = capture_start (STDOUT_FILENO, &fd_out_save);
203     aFile_err = capture_start (STDERR_FILENO, &fd_err_save);
204   }
205
206   // run command
207   try {
208     OCC_CATCH_SIGNALS
209
210     // get exception if control-break has been pressed 
211     OSD::ControlBreak();
212
213     // OCC63: Convert strings from UTF-8 to local encoding, normally expected by OCC commands
214     TclUTFToLocalStringSentry anArgs ( argc, (const char**)argv );
215       
216     Standard_Integer fres = C->f ( di, argc, anArgs.GetArgv() );
217     if (fres != 0) 
218       code = TCL_ERROR;
219   }
220   catch (Standard_Failure) {
221
222     Handle(Standard_Failure) E = Standard_Failure::Caught();
223
224     // fail if Draw_ExitOnCatch is set
225     // MKV 29.03.05
226 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
227     const char*  cc = Tcl_GetVar(interp,
228                           "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
229 #else
230     char* const cc = Tcl_GetVar(interp,
231                           "Draw_ExitOnCatch",TCL_GLOBAL_ONLY);
232 #endif
233
234     cout << "An exception was caught " << E << endl;
235
236     if (cc && atoi(cc)) {
237 #ifdef WNT
238       Tcl_Exit(0);
239 #else      
240       Tcl_Eval(interp,"exit");
241 #endif
242     }
243
244     // get the error message
245     Standard_SStream ss;
246     ss << "** Exception ** " << E << ends ;
247 #ifdef USE_STL_STREAM
248     Tcl_SetResult(interp,(char*)(ss.str().c_str()),TCL_VOLATILE);
249 #else
250     Tcl_SetResult(interp,(char*)(ss.str()),TCL_VOLATILE);
251 #endif    
252     code = TCL_ERROR;
253   }
254
255   // flush streams
256   flush_standard_streams();
257
258   // end capturing cout and cerr 
259   if (doLog) 
260   {
261     capture_end (aFile_err, STDERR_FILENO, fd_err_save, di.Log(), doEcho);
262     capture_end (aFile_out, STDOUT_FILENO, fd_out_save, di.Log(), doEcho);
263   }
264
265   // log command result
266   const char* aResultStr = NULL;
267   if (doLog)
268   {
269     aResultStr = Tcl_GetStringResult (interp);
270     if (aResultStr != 0 && aResultStr[0] != '\0' )
271       di.Log() << Tcl_GetStringResult (interp) << endl;
272   }
273   if (doEcho)
274   {
275     if (aResultStr == NULL)
276       aResultStr = Tcl_GetStringResult (interp);
277     if (aResultStr != 0 && aResultStr[0] != '\0' )
278       cout << Tcl_GetStringResult (interp) << endl;
279   }
280
281   return code;
282 }
283
284
285 static void CommandDelete (ClientData clientData)
286 {
287   CData *C = (CData*) clientData;
288   delete C;
289 }
290
291 //=======================================================================
292 //function : Draw_Interpretor
293 //purpose  : 
294 //=======================================================================
295
296 Draw_Interpretor::Draw_Interpretor() :
297   isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False)
298 {
299 // The tcl interpreter is not created immediately as it is kept 
300 // by a global variable and created and deleted before the main().
301   myInterp  = NULL;
302 }
303
304 //=======================================================================
305 //function : Init
306 //purpose  : It is necessary to call this function
307 //=======================================================================
308
309 void Draw_Interpretor::Init()
310 {
311   if (isAllocated) 
312     Tcl_DeleteInterp(myInterp);
313   isAllocated=Standard_True;
314   myInterp=Tcl_CreateInterp();
315 }
316
317 //=======================================================================
318 //function : Draw_Interpretor
319 //purpose  : 
320 //=======================================================================
321
322 Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
323   isAllocated(Standard_False),
324   myInterp(p),
325   myDoLog(Standard_False),
326   myDoEcho(Standard_False)
327 {
328 }
329
330 //=======================================================================
331 //function : Add
332 //purpose  : 
333 //=======================================================================
334 //#ifdef WNT
335 void Draw_Interpretor::Add(const Standard_CString n,
336                            const Standard_CString help,
337                            const Draw_CommandFunction f,
338                            const Standard_CString group)
339 //#else
340 //void Draw_Interpretor::Add(const Standard_CString n,
341 //                         const Standard_CString help,
342 //                         const Draw_CommandFunction& f,
343 //                         const Standard_CString group)
344 //#endif
345 {
346   Standard_PCharacter pN, pHelp, pGroup;
347   //
348   pN=(Standard_PCharacter)n;
349   pHelp=(Standard_PCharacter)help;
350   pGroup=(Standard_PCharacter)group;
351   //
352   if (myInterp==NULL) Init();
353
354   CData* C = new CData(f,this);
355   
356   Tcl_CreateCommand(myInterp, pN ,CommandCmd, (ClientData) C, CommandDelete);
357
358   // add the help
359   Tcl_SetVar2(myInterp,"Draw_Helps", pN, pHelp, TCL_GLOBAL_ONLY);
360   Tcl_SetVar2(myInterp,"Draw_Groups",pGroup,pN,
361               TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
362 }
363 //=======================================================================
364 //function : Add
365 //purpose  : 
366 //=======================================================================
367 void Draw_Interpretor::Add(const Standard_CString n,
368                            const Standard_CString help,
369                            const Standard_CString file_name,
370                            const Draw_CommandFunction f,
371                            const Standard_CString group)
372 {
373   Standard_PCharacter pN, pHelp, pGroup, pFileName;
374   //
375   pN=(Standard_PCharacter)n;
376   pHelp=(Standard_PCharacter)help;
377   pGroup=(Standard_PCharacter)group;
378   pFileName=(Standard_PCharacter)file_name;
379   //
380   if (myInterp==NULL) Init();
381
382   CData* C = new CData(f,this);
383   Tcl_CreateCommand(myInterp,pN,CommandCmd, (ClientData) C, CommandDelete);
384
385   // add the help
386   Tcl_SetVar2(myInterp,"Draw_Helps",pN,pHelp,TCL_GLOBAL_ONLY);
387   Tcl_SetVar2(myInterp,"Draw_Groups",pGroup,pN,
388               TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
389
390   // add path to source file (keep not more than two last subdirectories)
391   OSD_Path aPath (pFileName);
392   Standard_Integer nbTrek = aPath.TrekLength();
393   for (Standard_Integer i = 2; i < nbTrek; i++)
394     aPath.RemoveATrek (1);
395   aPath.SetDisk("");
396   aPath.SetNode("");
397   TCollection_AsciiString aSrcPath;
398   aPath.SystemName (aSrcPath);
399   Tcl_SetVar2(myInterp,"Draw_Files",pN,aSrcPath.ToCString(),TCL_GLOBAL_ONLY);
400 }
401
402
403 //=======================================================================
404 //function : Remove
405 //purpose  : 
406 //=======================================================================
407
408 Standard_Boolean Draw_Interpretor::Remove(Standard_CString const n)
409 {
410   Standard_PCharacter pN;
411   //
412   pN=(Standard_PCharacter)n;
413  
414   Standard_Integer result = Tcl_DeleteCommand(myInterp,pN);
415   return result == 0;
416 }
417
418 //=======================================================================
419 //function : Result
420 //purpose  : 
421 //=======================================================================
422
423 Standard_CString Draw_Interpretor::Result() const
424 {
425 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 5)))
426   return Tcl_GetStringResult(myInterp);
427 #else
428   return myInterp->result;
429 #endif
430 }
431
432 //=======================================================================
433 //function : Reset
434 //purpose  : 
435 //=======================================================================
436
437 void Draw_Interpretor::Reset()
438 {
439   Tcl_ResetResult(myInterp);
440 }
441
442 //=======================================================================
443 //function : Append
444 //purpose  : 
445 //=======================================================================
446
447 Draw_Interpretor& Draw_Interpretor::Append(const Standard_CString s)
448 {
449 #ifdef TCL_USES_UTF8
450   // Convert string to UTF-8 format for Tcl
451   Tcl_DString TclString;
452   Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
453   Tcl_AppendResult ( myInterp, Tcl_DStringValue ( &TclString ), (Standard_CString)0 );
454   Tcl_DStringFree ( &TclString );
455 #else
456   Tcl_AppendResult(myInterp,s,(Standard_CString)0);
457 #endif
458   return *this;
459 }
460
461 //=======================================================================
462 //function : Append
463 //purpose  : 
464 //=======================================================================
465
466 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_AsciiString& s)
467 {
468   return Append (s.ToCString());
469 }
470
471 //=======================================================================
472 //function : Append
473 //purpose  : 
474 //=======================================================================
475
476 Draw_Interpretor& Draw_Interpretor::Append(const TCollection_ExtendedString& theString)
477 {
478 #ifdef TCL_USES_UTF8
479   // Convert string to UTF-8 format for Tcl
480   char *str = new char[theString.LengthOfCString()+1];
481   theString.ToUTF8CString (str);
482   Tcl_AppendResult ( myInterp, str, (Standard_CString)0 );
483   delete[] str;
484 #else
485   // put as ascii string, replacing non-ascii characters by '?'
486   TCollection_AsciiString str (theString, '?');
487   Tcl_AppendResult(myInterp,str.ToCString(),(Standard_CString)0);
488 #endif
489   return *this;
490 }
491
492 //=======================================================================
493 //function : Append
494 //purpose  : 
495 //=======================================================================
496
497 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Integer i)
498 {
499   char c[100];
500   sprintf(c,"%d",i);
501   Tcl_AppendResult(myInterp,c,(Standard_CString)0);
502   return *this;
503 }
504
505 //=======================================================================
506 //function : Append
507 //purpose  : 
508 //=======================================================================
509
510 Draw_Interpretor& Draw_Interpretor::Append(const Standard_Real r)
511 {
512   char s[100];
513   sprintf(s,"%.17g",r);
514   Tcl_AppendResult(myInterp,s,(Standard_CString)0);
515   return *this;
516 }
517
518 //=======================================================================
519 //function : Append
520 //purpose  : 
521 //=======================================================================
522
523 Draw_Interpretor& Draw_Interpretor::Append(const Standard_SStream& s)
524 {
525 #ifdef USE_STL_STREAM
526   return Append (s.str().c_str());
527 #else
528   // Note: use dirty tricks -- unavoidable with old streams 
529   TCollection_AsciiString aStr (((Standard_SStream&)AReason).str(), AReason.pcount());
530   ((Standard_SStream&)AReason).freeze (false);
531   return Append (aStr.ToCString());
532 #endif
533 }
534
535 //=======================================================================
536 //function : AppendElement
537 //purpose  : 
538 //=======================================================================
539
540 void Draw_Interpretor::AppendElement(const Standard_CString s)
541 {
542 #ifdef TCL_USES_UTF8
543   // Convert string to UTF-8 format for Tcl
544   Tcl_DString TclString;
545   Tcl_ExternalToUtfDString ( NULL, s, -1, &TclString );
546   Tcl_AppendElement ( myInterp, Tcl_DStringValue ( &TclString ) );
547   Tcl_DStringFree ( &TclString );
548 #else
549 #ifdef IRIX
550   //AppendElement is declared as (Tcl_Interp *interp, char *string)
551   //on SGI 32
552   Tcl_AppendElement(myInterp,(char*) s);
553 #else
554   Tcl_AppendElement(myInterp, s);
555 #endif
556 #endif
557 }
558
559 //=======================================================================
560 //function : Eval
561 //purpose  : 
562 //=======================================================================
563
564 Standard_Integer Draw_Interpretor::Eval(const Standard_CString line)
565 {
566   Standard_PCharacter pLine;
567   //
568   pLine=(Standard_PCharacter)line;
569   //
570   return Tcl_Eval(myInterp,pLine);
571 }
572
573
574 //=======================================================================
575 //function : Eval
576 //purpose  : 
577 //=======================================================================
578
579 Standard_Integer Draw_Interpretor::RecordAndEval(const Standard_CString line,
580                                                  const Standard_Integer flags)
581 {
582   Standard_PCharacter pLine;
583   //
584   pLine=(Standard_PCharacter)line;
585   return Tcl_RecordAndEval(myInterp,pLine,flags);
586 }
587
588 //=======================================================================
589 //function : EvalFile
590 //purpose  : 
591 //=======================================================================
592
593 Standard_Integer Draw_Interpretor::EvalFile(const Standard_CString fname)
594 {
595   Standard_PCharacter pfname;
596   //
597   pfname=(Standard_PCharacter)fname;
598   return Tcl_EvalFile(myInterp,pfname);
599 }
600
601 //=======================================================================
602 //function :Complete
603 //purpose  : 
604 //=======================================================================
605
606 Standard_Boolean Draw_Interpretor::Complete(const Standard_CString line)
607 {
608   Standard_PCharacter pLine;
609   //
610   pLine=(Standard_PCharacter)line;
611   return Tcl_CommandComplete(pLine);
612 }
613
614 //=======================================================================
615 //function : Destroy
616 //purpose  : 
617 //=======================================================================
618
619 void Draw_Interpretor::Destroy()
620 {
621   // MKV 01.02.05
622 #if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4)))
623   try {
624     OCC_CATCH_SIGNALS
625     Tcl_Exit(0);
626   }
627   catch (Standard_Failure) {
628 #ifdef DEB
629     cout <<"Tcl_Exit have an exeption" << endl;
630 #endif
631   }
632 #else
633 #ifdef WNT
634   Tcl_Exit(0);
635 #endif  
636 #endif
637 }
638
639 //=======================================================================
640 //function : Interp
641 //purpose  : 
642 //=======================================================================
643
644 Draw_PInterp Draw_Interpretor::Interp() const
645 {
646   Standard_DomainError_Raise_if (myInterp==NULL , "No call for  Draw_Interpretor::Init()");
647   return myInterp;
648 }
649
650 void Draw_Interpretor::Set(const Draw_PInterp& PIntrp)
651 {
652   if (isAllocated)
653     Tcl_DeleteInterp(myInterp);
654   isAllocated = Standard_False;
655   myInterp = PIntrp;
656 }
657
658 //=======================================================================
659 //function : Logging
660 //purpose  : 
661 //=======================================================================
662
663 void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
664 {
665   myDoLog = doLog;
666 }
667
668 void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
669 {
670   myDoEcho = doEcho;
671 }
672
673 Standard_Boolean Draw_Interpretor::GetDoLog () const
674 {
675   return myDoLog;
676 }
677
678 Standard_Boolean Draw_Interpretor::GetDoEcho () const
679 {
680   return myDoEcho;
681 }
682
683 Standard_SStream& Draw_Interpretor::Log ()
684 {
685   return myLog;
686 }