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