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