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