0032525: Data Exchange, RWGltf_CafReader - support KHR_draco_mesh_compression
[occt.git] / src / Draw / Draw_BasicCommands.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
18 #include <Draw.hxx>
19 #include <Draw_Appli.hxx>
20 #include <Draw_Chronometer.hxx>
21 #include <Draw_Drawable3D.hxx>
22 #include <Draw_Printer.hxx>
23 #include <Draw_ProgressIndicator.hxx>
24 #include <Message.hxx>
25 #include <Message_Messenger.hxx>
26 #include <Message_PrinterOStream.hxx>
27 #include <OSD.hxx>
28 #include <OSD_Chronometer.hxx>
29 #include <OSD_Environment.hxx>
30 #include <OSD_Exception_CTRL_BREAK.hxx>
31 #include <OSD_MAllocHook.hxx>
32 #include <OSD_MemInfo.hxx>
33 #include <OSD_Parallel.hxx>
34 #include <OSD_ThreadPool.hxx>
35 #include <Standard_Macro.hxx>
36 #include <Standard_SStream.hxx>
37 #include <Standard_Stream.hxx>
38 #include <Standard_Version.hxx>
39 #include <TCollection_AsciiString.hxx>
40
41 #include <OSD_PerfMeter.h>
42 #ifdef _WIN32
43
44 #include <windows.h>
45 #include <winbase.h>
46 #include <process.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <time.h>
50 #include <limits>
51
52 #define RLIM_INFINITY   0x7fffffff
53
54 static clock_t CPU_CURRENT; // cpu time already used at last
55                             // cpulimit call. (sec.) 
56 #else /* _WIN32 */
57
58 #include <sys/resource.h>
59 #include <signal.h>
60 #include <unistd.h>
61
62 #if defined (__hpux) || defined ( HPUX )
63 #define RLIM_INFINITY   0x7fffffff
64 #define RLIMIT_CPU      0
65 #endif
66
67 #endif /* _WIN32 */
68
69 extern Standard_Boolean Draw_Batch;
70
71 static clock_t CPU_LIMIT;   // Cpu_limit in Sec.
72 static OSD_Timer aTimer;
73
74 //=======================================================================
75 // chronom
76 //=======================================================================
77
78 extern Standard_Boolean Draw_Chrono;
79
80 static Standard_Integer chronom(Draw_Interpretor& di,
81                                 Standard_Integer n,const char** a)
82 {
83   if ((n == 1) || (*a[1] == '0') || (*a[1] == '1')) {
84     if (n == 1)
85       Draw_Chrono = !Draw_Chrono;
86     else
87       Draw_Chrono = (*a[1] == '1');
88
89     if (Draw_Chrono) di << "Chronometers activated.\n";
90     else di << "Chronometers deactivated.\n";
91   }
92   else {
93     Handle(Draw_Drawable3D) D = Draw::Get(a[1]);
94     Handle(Draw_Chronometer) C;
95     if (!D.IsNull()) {
96       C = Handle(Draw_Chronometer)::DownCast(D);
97     }
98     if (C.IsNull()) {
99       C = new Draw_Chronometer();
100     Draw::Set(a[1],C,Standard_False);
101     }
102     if (n <= 2) {
103       C->Timer().Reset();
104     }
105     else {
106       for (Standard_Integer anIter = 2; anIter < n; ++anIter)
107       {
108         TCollection_AsciiString anArg (a[anIter]);
109         anArg.LowerCase();
110
111         if (anArg == "reset")
112         {
113           C->Timer().Reset();
114         }
115         else if (anArg == "restart")
116         {
117           C->Timer().Restart();
118         }
119         else if (anArg == "start")
120         {
121           C->Timer().Start();
122         }
123         else if (anArg == "stop")
124         {
125           C->Timer().Stop();
126         }
127         else if (anArg == "show")
128         {
129           C->Timer().Show();
130         }
131         else if (anArg == "counter")
132         {
133           Standard_Real aSeconds,aCPUtime;
134           Standard_Integer aMinutes, aHours;
135           C->Timer().Show(aSeconds,aMinutes,aHours,aCPUtime);
136           std::cout << "COUNTER " << a[++anIter] << ": " << aCPUtime << "\n";
137         }
138         else
139         {
140           std::cerr << "Unknown argument '" << a[anIter] << "'!\n";
141         }
142       }
143     }
144   }
145   return 0;
146 }
147
148 static Standard_Integer dchronom(Draw_Interpretor& theDI,
149                                  Standard_Integer n,const char** a)
150 {
151   if ((n == 1) || (*a[1] == '0') || (*a[1] == '1')) {
152     if (n == 1)
153       Draw_Chrono = !Draw_Chrono;
154     else
155       Draw_Chrono = (*a[1] == '1');
156
157     if (Draw_Chrono) theDI << "Chronometers activated.\n";
158     else theDI << "Chronometers deactivated.\n";
159   }
160   else {
161     Handle(Draw_Drawable3D) D = Draw::Get(a[1]);
162     Handle(Draw_Chronometer) C;
163     if (!D.IsNull()) {
164       C = Handle(Draw_Chronometer)::DownCast(D);
165     }
166     if (C.IsNull()) {
167       C = new Draw_Chronometer();
168       Draw::Set(a[1],C,Standard_False);
169     }
170     if (n <= 2) {
171       C->Timer().Reset();
172     }
173     else {
174       for (Standard_Integer anIter = 2; anIter < n; ++anIter)
175       {
176         TCollection_AsciiString anArg (a[anIter]);
177         anArg.LowerCase();
178
179         if (anArg == "reset")
180         {
181           C->Timer().Reset();
182         }
183         else if (anArg == "restart")
184         {
185           C->Timer().Restart();
186         }
187         else if (anArg == "start")
188         {
189           C->Timer().Start();
190         }
191         else if (anArg == "stop")
192         {
193           C->Timer().Stop();
194         }
195         else if (anArg == "show")
196         {
197           Standard_SStream ss;
198           C->Timer().Show(ss);
199           theDI << ss;
200         }
201         else if (anArg == "counter")
202         {
203           Standard_Real aSeconds,aCPUtime;
204           Standard_Integer aMinutes, aHours;
205           C->Timer().Show(aSeconds,aMinutes,aHours,aCPUtime);
206           theDI << "COUNTER " << a[++anIter] << ": " << aCPUtime << "\n";
207         }
208         else
209         {
210           theDI << "Unknown argument '" << a[anIter] << "'!\n";
211         }
212       }
213     }
214   }
215   return 0;
216 }
217
218
219
220 //=======================================================================
221 //function : ifbatch
222 //purpose  : 
223 //=======================================================================
224
225 static Standard_Integer ifbatch(Draw_Interpretor& DI, Standard_Integer , const char** )
226 {
227   if (Draw_Batch)
228     DI << "1";
229   else
230     DI << "0";
231   
232   return 0;
233 }
234
235 //=======================================================================
236 //function : spy
237 //purpose  : 
238 //=======================================================================
239
240 extern Standard_Boolean Draw_Spying;
241 extern std::filebuf Draw_Spyfile;
242
243 static Standard_Integer spy(Draw_Interpretor& di, Standard_Integer n, const char** a)
244 {
245   if (Draw_Spying) 
246     Draw_Spyfile.close();
247   Draw_Spying = Standard_False;
248   if (n > 1) {
249     if (!Draw_Spyfile.open(a[1],std::ios::out)) {
250       di << "Cannot open "<<a[1]<<" for writing\n";
251       return 1;
252     }
253     Draw_Spying = Standard_True;
254   }
255   return 0;
256 }
257
258 static Standard_Integer dlog(Draw_Interpretor& di, Standard_Integer n, const char** a)
259 {
260   if (n != 2 && n != 3)
261   {
262     Message::SendFail() << "Enable or disable logging: " << a[0] << " {on|off}\n"
263                         << "Reset log: " << a[0] << " reset\n"
264                         << "Get log content: " << a[0] << " get";
265     return 1;
266   }
267
268   if (! strcmp (a[1], "on") && n == 2)
269   {
270     di.SetDoLog (Standard_True);
271 //    di.Log() << "dlog on" << std::endl; // for symmetry
272   }
273   else if (! strcmp (a[1], "off") && n == 2)
274   {
275     di.SetDoLog (Standard_False);
276   }
277   else if (! strcmp (a[1], "reset") && n == 2)
278   {
279     di.ResetLog();
280   }
281   else if (! strcmp (a[1], "get") && n == 2)
282   {
283     di << di.GetLog();
284   }
285   else if (! strcmp (a[1], "add") && n == 3)
286   {
287     di.AddLog (a[2]);
288     di.AddLog ("\n");
289   }
290   else if (! strcmp (a[1], "status") && n == 2)
291   {
292     di << (di.GetDoLog() ? "on" : "off");
293   }
294   else {
295     Message::SendFail() << "Unrecognized option(s): " << a[1];
296     return 1;
297   }
298   return 0;
299 }
300
301 static Standard_Integer decho(Draw_Interpretor& di, Standard_Integer n, const char** a)
302 {
303   if (n != 2)
304   {
305     Message::SendFail() << "Enable or disable echoing: " << a[0] << " {on|off}";
306     return 1;
307   }
308
309   if (! strcmp (a[1], "on"))
310   {
311     di.SetDoEcho (Standard_True);
312   }
313   else if (! strcmp (a[1], "off"))
314   {
315     di.SetDoEcho (Standard_False);
316   }
317   else {
318     Message::SendFail() << "Unrecognized option: " << a[1];
319     return 1;
320   }
321   return 0;
322 }
323
324 static Standard_Integer dbreak(Draw_Interpretor& di, Standard_Integer, const char**)
325 {
326   try {
327     OSD::ControlBreak();
328   }
329   catch (OSD_Exception_CTRL_BREAK const&) {
330     di << "User pressed Control-Break";
331     return 1; // Tcl exception
332   }
333
334   return 0;
335 }
336
337 static Standard_Integer dversion(Draw_Interpretor& di, Standard_Integer, const char**)
338 {
339   // print OCCT version and OCCTY-specific macros used
340   di << "Open CASCADE Technology " << OCC_VERSION_STRING_EXT << "\n";
341 #ifdef OCCT_DEBUG
342   di << "Extended debug mode\n";
343 #elif defined(_DEBUG)
344   di << "Debug mode\n";
345 #endif
346 #ifdef HAVE_TK
347   di << "Tk enabled (HAVE_TK)\n";
348 #else
349   di << "Tk disabled\n";
350 #endif
351 #ifdef HAVE_XLIB
352   di << "Xlib enabled (HAVE_XLIB)\n";
353 #elif !defined(_WIN32)
354   di << "Xlib disabled\n";
355 #endif
356 #ifdef HAVE_TBB
357   di << "TBB enabled (HAVE_TBB)\n";
358 #else 
359   di << "TBB disabled\n";
360 #endif
361 #ifdef HAVE_FREETYPE
362   di << "FreeType enabled (HAVE_FREETYPE)\n";
363 #else
364   di << "FreeType disabled\n";
365 #endif
366 #ifdef HAVE_FREEIMAGE
367   di << "FreeImage enabled (HAVE_FREEIMAGE)\n";
368 #else
369   di << "FreeImage disabled\n";
370 #endif
371 #ifdef HAVE_FFMPEG
372   di << "FFmpeg enabled (HAVE_FFMPEG)\n";
373 #else
374   di << "FFmpeg disabled\n";
375 #endif
376 #ifdef HAVE_OPENGL_EXT
377   di << "OpenGL: enabled (HAVE_OPENGL_EXT)\n";
378 #endif
379 #ifdef HAVE_GLES2_EXT
380   di << "OpenGL ES: enabled (HAVE_GLES2_EXT)\n";
381 #endif
382 #ifdef HAVE_OPENVR
383   di << "OpenVR enabled (HAVE_OPENVR)\n";
384 #else
385   di << "OpenVR disabled\n";
386 #endif
387 #ifdef HAVE_RAPIDJSON
388   di << "RapidJSON enabled (HAVE_RAPIDJSON)\n";
389 #else
390   di << "RapidJSON disabled\n";
391 #endif
392 #ifdef HAVE_DRACO
393   di << "Draco enabled (HAVE_DRACO)\n";
394 #else
395   di << "Draco disabled\n";
396 #endif
397 #ifdef HAVE_VTK
398   di << "VTK enabled (HAVE_VTK)\n";
399 #else
400   di << "VTK disabled\n";
401 #endif
402 #ifdef No_Exception
403   di << "Exceptions disabled (No_Exception)\n";
404 #else
405   di << "Exceptions enabled\n";
406 #endif
407
408   // check compiler, OS, etc. using pre-processor macros provided by compiler
409   // see "Pre-defined C/C++ Compiler Macros" http://sourceforge.net/p/predef/wiki/
410   // note that only modern compilers that are known to be used for OCCT are recognized
411
412   // compiler; note that GCC and MSVC are last as other compilers (e.g. Intel) can also define __GNUC__ and _MSC_VER
413 #if defined(__INTEL_COMPILER)
414   di << "Compiler: Intel " << __INTEL_COMPILER << "\n";
415 #elif defined(__BORLANDC__)
416   di << "Compiler: Borland C++ (__BORLANDC__ = " << __BORLANDC__ << ")\n";
417 #elif defined(__clang__)
418   di << "Compiler: Clang " << __clang_major__ << "." << __clang_minor__ << "." << __clang_patchlevel__ << "\n";
419 #elif defined(__SUNPRO_C)
420   di << "Compiler: Sun Studio (__SUNPRO_C = " << __SUNPROC_C << ")\n";
421 #elif defined(_MSC_VER)
422   #if _MSC_VER < 1900
423     di << "Compiler: MS Visual C++ " << (int)(_MSC_VER/100-6) << "." << (int)((_MSC_VER/10)-60-10*(int)(_MSC_VER/100-6)) << " (_MSC_FULL_VER = " << _MSC_FULL_VER << ")\n";
424   #else
425     di << "Compiler: MS Visual C++ " << (int)(_MSC_VER/100-5) << "." << (int)((_MSC_VER/10)-50-10*(int)(_MSC_VER/100-5)) << " (_MSC_FULL_VER = " << _MSC_FULL_VER << ")\n";
426   #endif
427 #elif defined(__GNUC__)
428   di << "Compiler: GCC " << __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__ << "\n";
429 #else
430   di << "Compiler: unrecognized\n";
431 #endif
432
433   // Cygwin and MinGW specifics
434 #if defined(__CYGWIN__)
435   di << "Cygwin\n";
436 #endif
437 #if defined(__MINGW64__)
438   di << "MinGW 64 " << __MINGW64_VERSION_MAJOR << "." << __MINGW64_VERSION_MINOR << "\n";
439 #elif defined(__MINGW32__)
440   di << "MinGW 32 " << __MINGW32_MAJOR_VERSION << "." << __MINGW32_MINOR_VERSION << "\n";
441 #endif 
442
443   // architecture
444 #if defined(__amd64) || defined(__x86_64) || defined(_M_AMD64)
445   di << "Architecture: AMD64\n";
446 #elif defined(__i386) || defined(_M_IX86) || defined(__X86__)|| defined(_X86_)
447   di << "Architecture: Intel x86\n";
448 #elif defined(_M_IA64) || defined(__ia64__)
449   di << "Architecture: Intel Itanium (IA 64)\n";
450 #elif defined(__sparc__) || defined(__sparc)
451   di << "Architecture: SPARC\n";
452 #elif defined(__aarch64__) && defined(__LP64__)
453   di << "Architecture: ARM 64-bit\n";
454 #elif defined(__arm__) || defined(__arm64__)
455   #if defined(__LP64__)
456   di << "Architecture: ARM 64-bit\n";
457   #else
458   di << "Architecture: ARM 32-bit\n";
459   #endif
460 #elif defined(__EMSCRIPTEN__)
461   di << "Architecture: WASM "
462   #if defined(__LP64__)
463      << "64-bit"
464   #else
465      << "32-bit"
466   #endif
467   #if defined(__wasm_simd128__)
468      << " SIMD128"
469   #endif
470      << "\n";
471 #else
472   di << "Architecture: unrecognized\n";
473 #endif
474
475   // OS
476 #if defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__)
477   di << "OS: Windows\n";
478 #elif defined(__APPLE__) || defined(__MACH__)
479   di << "OS: Mac OS X\n";
480 #elif defined(__sun) 
481   di << "OS: SUN Solaris\n";
482 #elif defined(__ANDROID__) /* must be before Linux */
483   #include <android/api-level.h>
484   di << "OS: Android (__ANDROID_API__ = " << __ANDROID_API__ << ")\n";
485 #elif defined(__QNXNTO__)
486   di << "OS: QNX Neutrino\n";
487 #elif defined(__QNX__)
488   di << "OS: QNX\n";
489 #elif defined(__linux__)
490   di << "OS: Linux\n";
491 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
492   #include <sys/param.h>
493   di << "OS: BSD (BSD = " << BSD << ")\n";
494 #elif defined(__EMSCRIPTEN__)
495   di << "OS: WebAssembly (Emscripten SDK " << __EMSCRIPTEN_major__ << "." << __EMSCRIPTEN_minor__ << "." << __EMSCRIPTEN_tiny__
496   #ifdef __EMSCRIPTEN_PTHREADS__
497      << "; pthreads ON"
498   #else
499      << "; pthreads OFF"
500   #endif
501      << ")\n";
502 #else
503   di << "OS: unrecognized\n";
504 #endif
505
506   return 0;
507 }
508
509 //=======================================================================
510 //function : wait
511 //purpose  : 
512 //=======================================================================
513
514 static Standard_Integer Draw_wait(Draw_Interpretor& , Standard_Integer n, const char** a)
515 {
516   Standard_Integer w = 10;
517   if (n > 1)
518     w = Draw::Atoi(a[1]);
519   time_t ct = time(NULL) + w;
520   while (time(NULL) < ct) {};
521   return 0;
522 }
523
524 //=======================================================================
525 //function : cpulimit
526 //purpose  : 
527 //=======================================================================
528 #ifdef _WIN32
529 static unsigned int __stdcall CpuFunc (void * /*param*/)
530 {
531   clock_t anElapCurrent;
532   clock_t aCurrent;
533
534   for(;;)
535   {
536     Sleep (5);
537     Standard_Real anUserSeconds, aSystemSeconds;
538     OSD_Chronometer::GetProcessCPU (anUserSeconds, aSystemSeconds);
539     aCurrent = clock_t(anUserSeconds + aSystemSeconds);
540     anElapCurrent = clock_t(aTimer.ElapsedTime());
541     
542     if (CPU_LIMIT > 0 && (aCurrent - CPU_CURRENT) >= CPU_LIMIT)
543     {
544       aTimer.Stop();
545       if (IsDebuggerPresent())
546       {
547         std::cout << "Info: CPU limit (" << CPU_LIMIT << " sec) has been reached but ignored because of attached Debugger" << std::endl;
548         return 0;
549       }
550       else
551       {
552         std::cout << "ERROR: Process killed by CPU limit (" << CPU_LIMIT << " sec)" << std::endl;
553         ExitProcess (2);
554       }
555     }
556     if (CPU_LIMIT > 0 && anElapCurrent >= CPU_LIMIT)
557     {
558       aTimer.Stop();
559       if (IsDebuggerPresent())
560       {
561         std::cout << "Info: Elapsed limit (" << CPU_LIMIT << " sec) has been reached but ignored because of attached Debugger" << std::endl;
562         return 0;
563       }
564       else
565       {
566         std::cout << "ERROR: Process killed by elapsed limit (" << CPU_LIMIT << " sec)" << std::endl;
567         ExitProcess (2);
568       }
569     }
570   }
571 }
572 #else
573 static void cpulimitSignalHandler (int)
574 {
575   std::cout << "Process killed by CPU limit  (" << CPU_LIMIT << " sec)" << std::endl;
576   exit(2);
577 }
578 static void *CpuFunc(void* /*threadarg*/)
579 {
580   clock_t anElapCurrent;
581   for(;;)
582   {
583     sleep (5);
584     anElapCurrent = clock_t(aTimer.ElapsedTime());
585     if (CPU_LIMIT >0 && (anElapCurrent) >= CPU_LIMIT) {
586       std::cout << "Process killed by elapsed limit  (" << CPU_LIMIT << " sec)" << std::endl;
587       exit(2);
588     }
589   }
590   return NULL;
591 }
592 #endif
593
594 // Returns time in seconds defined by the argument string,
595 // multiplied by factor defined in environment variable
596 // CSF_CPULIMIT_FACTOR (if it exists, 1 otherwise)
597 static clock_t GetCpuLimit (const Standard_CString theParam)
598 {
599   clock_t aValue = Draw::Atoi (theParam);
600
601   OSD_Environment aEnv("CSF_CPULIMIT_FACTOR");
602   TCollection_AsciiString aEnvStr = aEnv.Value();
603   if (!aEnvStr.IsEmpty())
604   {
605     aValue *= Draw::Atoi (aEnvStr.ToCString());
606   }
607   return aValue;
608 }
609
610 static Standard_Integer cpulimit(Draw_Interpretor& di, Standard_Integer n, const char** a)
611 {
612   static int aFirst = 1;
613 #ifdef _WIN32
614   // Windows specific code
615   unsigned int __stdcall CpuFunc (void *);
616   unsigned aThreadID;
617
618   if (n <= 1){
619     CPU_LIMIT = RLIM_INFINITY;
620   } else {
621     CPU_LIMIT = GetCpuLimit (a[1]);
622     Standard_Real anUserSeconds, aSystemSeconds;
623     OSD_Chronometer::GetProcessCPU (anUserSeconds, aSystemSeconds);
624     CPU_CURRENT = clock_t(anUserSeconds + aSystemSeconds);
625     aTimer.Reset();
626     aTimer.Start();
627     if (aFirst) // Launch the thread only at the 1st call.
628     {
629       aFirst = 0;
630       _beginthreadex (NULL, 0, CpuFunc, NULL, 0, &aThreadID);
631     }
632   }
633
634 #else 
635   // Unix & Linux
636   rlimit rlp;
637   rlp.rlim_max = RLIM_INFINITY;
638   if (n <= 1)
639     rlp.rlim_cur = RLIM_INFINITY;
640   else
641     rlp.rlim_cur = GetCpuLimit (a[1]);
642   CPU_LIMIT = rlp.rlim_cur;
643
644   int status;
645   status=setrlimit(RLIMIT_CPU,&rlp);
646   if (status !=0)
647     di << "status cpulimit setrlimit : " << status << "\n";
648
649   // set signal handler to print a message before death
650   struct sigaction act, oact;
651   memset (&act, 0, sizeof(act));
652   act.sa_handler = cpulimitSignalHandler;
653   sigaction (SIGXCPU, &act, &oact);
654
655   // cpulimit for elapsed time
656   aTimer.Reset();
657   aTimer.Start();
658   pthread_t cpulimitThread;
659   if (aFirst) // Launch the thread only at the 1st call.
660   {
661     aFirst = 0;
662     pthread_create(&cpulimitThread, NULL, CpuFunc, NULL);
663   }
664 #endif
665   di << "CPU and elapsed time limit set to " << (double)CPU_LIMIT << " seconds";
666   return 0;
667 }
668
669 //=======================================================================
670 //function : mallochook
671 //purpose  : 
672 //=======================================================================
673
674 static Standard_Integer mallochook(Draw_Interpretor& di, Standard_Integer n,
675                                    const char** a)
676 {
677   if (n < 2)
678   {
679     di << "\
680 usage: mallochook cmd\n\
681 where cmd is one of:\n\
682   set [<op>]      - set callback to malloc/free; op is one of the following:\n\
683                     0 - set callback to NULL,\n\
684                     1 - set callback OSD_MAllocHook::CollectBySize (default)\n\
685                     2 - set callback OSD_MAllocHook::LogFileHandler\n\
686   reset           - reset the CollectBySize handler\n\
687   report1 [<outfile>]\n\
688                   - write report from CollectBySize handler in <outfile>\n\
689   open [<logfile>]\n\
690                   - open file for writing the log with LogFileHandler\n\
691   close           - close the log file with LogFileHandler\n\
692   report2 [<flag>] [<logfile>] [<outfile>]\n\
693                   - scan <logfile> written with LogFileHandler\n\
694                     and make synthesized report in <outfile>; <flag> can be:\n\
695                     0 - simple stats by sizes (default),\n\
696                     1 - with alive allocation numbers\n\
697 By default <logfile> is \"mem-log.txt\", <outfile> is \"mem-stat.txt\""
698       << "\n";
699     return 0;
700   }
701   if (strcmp(a[1], "set") == 0)
702   {
703     int aType = (n > 2 ? Draw::Atoi(a[2]) : 1);
704     if (aType < 0 || aType > 2)
705     {
706       di << "unknown op of the command set\n";
707       return 1;
708     }
709     else if (aType == 0)
710     {
711       OSD_MAllocHook::SetCallback(NULL);
712       di << "callback is unset\n";
713     }
714     else if (aType == 1)
715     {
716       OSD_MAllocHook::SetCallback(OSD_MAllocHook::GetCollectBySize());
717       di << "callback is set to CollectBySize\n";
718     }
719     else //if (aType == 2)
720     {
721       OSD_MAllocHook::SetCallback(OSD_MAllocHook::GetLogFileHandler());
722       di << "callback is set to LogFileHandler\n";
723     }
724   }
725   else if (strcmp(a[1], "reset") == 0)
726   {
727     OSD_MAllocHook::GetCollectBySize()->Reset();
728     di << "CollectBySize handler is reset\n";
729   }
730   else if (strcmp(a[1], "open") == 0)
731   {
732     const char* aFileName = (n > 2 ? a[2] : "mem-log.txt");
733     if (!OSD_MAllocHook::GetLogFileHandler()->Open(aFileName))
734     {
735       di << "cannot create file " << aFileName << " for writing\n";
736       return 1;
737     }
738     di << "log file " << aFileName << " is opened for writing\n";
739   }
740   else if (strcmp(a[1], "close") == 0)
741   {
742     OSD_MAllocHook::GetLogFileHandler()->Close();
743     di << "log file is closed\n";
744   }
745   else if (strcmp(a[1], "report1") == 0)
746   {
747     const char* aOutFile = "mem-stat.txt";
748     if (n > 2)
749       aOutFile = a[2];
750     if (OSD_MAllocHook::GetCollectBySize()->MakeReport(aOutFile))
751     {
752       di << "report " << aOutFile << " has been created\n";
753     }
754     else
755     {
756       di << "cannot create report " << aOutFile << "\n";
757       return 1;
758     }
759   }
760   else if (strcmp(a[1], "report2") == 0)
761   {
762     Standard_Boolean includeAlive = Standard_False;
763     const char* aLogFile = "mem-log.txt";
764     const char* aOutFile = "mem-stat.txt";
765     if (n > 2)
766     {
767       includeAlive = (Draw::Atoi(a[2]) != 0);
768       if (n > 3)
769       {
770         aLogFile = a[3];
771         if (n > 4)
772           aOutFile = a[4];
773       }
774     }
775     if (OSD_MAllocHook::LogFileHandler::MakeReport(aLogFile, aOutFile, includeAlive))
776     {
777       di << "report " << aOutFile << " has been created\n";
778     }
779     else
780     {
781       di << "cannot create report " << aOutFile << " from the log file "
782         << aLogFile << "\n";
783       return 1;
784     }
785   }
786   else
787   {
788     di << "unrecognized command " << a[1] << "\n";
789     return 1;
790   }
791   return 0;
792 }
793
794 //==============================================================================
795 //function : dlocale
796 //purpose  :
797 //==============================================================================
798
799 static int dlocale (Draw_Interpretor& di, Standard_Integer n, const char** argv)
800 {
801   int category = LC_ALL;
802   if (n > 1)
803   {
804     const char *cat = argv[1];
805     if ( ! strcmp (cat, "LC_ALL") ) category = LC_ALL;
806     else if ( ! strcmp (cat, "LC_COLLATE") ) category = LC_COLLATE;
807     else if ( ! strcmp (cat, "LC_CTYPE") ) category = LC_CTYPE;
808     else if ( ! strcmp (cat, "LC_MONETARY") ) category = LC_MONETARY;
809     else if ( ! strcmp (cat, "LC_NUMERIC") ) category = LC_NUMERIC;
810     else if ( ! strcmp (cat, "LC_TIME") ) category = LC_TIME;
811     else 
812     {
813       Message::SendFail() << "Error: cannot recognize argument " << cat << " as one of LC_ macros";
814       return 1;
815     }
816   }
817   const char* locale = (n > 2 ? argv[2] : NULL);
818   const char* result = setlocale (category, locale);
819   if (result)
820     di << result;
821   else 
822     std::cout << "Error: unsupported locale specification: " << locale << std::endl;
823   return 0;
824 }
825
826 //==============================================================================
827 //function : dmeminfo
828 //purpose  :
829 //==============================================================================
830
831 static int dmeminfo (Draw_Interpretor& theDI,
832                      Standard_Integer  theArgNb,
833                      const char**      theArgVec)
834 {
835   if (theArgNb <= 1)
836   {
837     OSD_MemInfo aMemInfo;
838     theDI << aMemInfo.ToString();
839     return 0;
840   }
841
842   NCollection_Map<OSD_MemInfo::Counter> aCounters;
843   for (Standard_Integer anIter = 1; anIter < theArgNb; ++anIter)
844   {
845     TCollection_AsciiString anArg (theArgVec[anIter]);
846     anArg.LowerCase();
847     if (anArg == "virt" || anArg == "v")
848     {
849       aCounters.Add (OSD_MemInfo::MemVirtual);
850     }
851     else if (anArg == "heap" || anArg == "h")
852     {
853       aCounters.Add (OSD_MemInfo::MemHeapUsage);
854     }
855     else if (anArg == "wset" || anArg == "w")
856     {
857       aCounters.Add (OSD_MemInfo::MemWorkingSet);
858     }
859     else if (anArg == "wsetpeak")
860     {
861       aCounters.Add (OSD_MemInfo::MemWorkingSetPeak);
862     }
863     else if (anArg == "swap")
864     {
865       aCounters.Add (OSD_MemInfo::MemSwapUsage);
866     }
867     else if (anArg == "swappeak")
868     {
869       aCounters.Add (OSD_MemInfo::MemSwapUsagePeak);
870     }
871     else if (anArg == "private")
872     {
873       aCounters.Add (OSD_MemInfo::MemPrivate);
874     }
875     else
876     {
877       std::cerr << "Unknown argument '" << theArgVec[anIter] << "'!\n";
878     }
879   }
880
881   OSD_MemInfo aMemInfo (Standard_False);
882   aMemInfo.SetActive (Standard_False);
883   for (NCollection_Map<OSD_MemInfo::Counter>::Iterator aCountersIt (aCounters); aCountersIt.More(); aCountersIt.Next())
884   {
885     aMemInfo.SetActive (aCountersIt.Value(), Standard_True);
886   }
887   aMemInfo.Update();
888
889   for (NCollection_Map<OSD_MemInfo::Counter>::Iterator aCountersIt (aCounters); aCountersIt.More(); aCountersIt.Next())
890   {
891     theDI << Standard_Real (aMemInfo.Value (aCountersIt.Value())) << " ";
892   }
893   theDI << "\n";
894   return 0;
895 }
896
897 //==============================================================================
898 //function : dparallel
899 //purpose  :
900 //==============================================================================
901 static int dparallel (Draw_Interpretor& theDI,
902                       Standard_Integer  theArgNb,
903                       const char**      theArgVec)
904 {
905   const Handle(OSD_ThreadPool)& aDefPool = OSD_ThreadPool::DefaultPool();
906   if (theArgNb <= 1)
907   {
908     theDI << "NbLogicalProcessors: " << OSD_Parallel::NbLogicalProcessors() << "\n"
909           << "NbThreads:           " << aDefPool->NbThreads() << "\n"
910           << "NbDefThreads:        " << aDefPool->NbDefaultThreadsToLaunch() << "\n"
911           << "UseOcct:             " << (OSD_Parallel::ToUseOcctThreads() ? 1 : 0);
912     return 0;
913   }
914
915   for (Standard_Integer anIter = 1; anIter < theArgNb; ++anIter)
916   {
917     TCollection_AsciiString anArg (theArgVec[anIter]);
918     anArg.LowerCase();
919     if (anIter + 1 < theArgNb
920      && (anArg == "-nbthreads"
921       || anArg == "-threads"))
922     {
923       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
924       aDefPool->Init (aVal);
925     }
926     else if (anIter + 1 < theArgNb
927           && (anArg == "-nbdefthreads"
928            || anArg == "-defthreads"
929            || anArg == "-nbmaxdefthreads"
930            || anArg == "-maxdefthreads"))
931     {
932       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
933       if (aVal <= 0 || aVal > aDefPool->NbThreads())
934       {
935         Message::SendFail() << "Syntax error: maximum number of threads to use should be <= of threads in the pool";
936         return 1;
937       }
938       aDefPool->SetNbDefaultThreadsToLaunch (aVal);
939     }
940     else if (anIter + 1 < theArgNb
941           && (anArg == "-useocct"
942            || anArg == "-touseocct"
943            || anArg == "-occt"))
944     {
945       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
946       OSD_Parallel::SetUseOcctThreads (aVal == 1);
947       if (OSD_Parallel::ToUseOcctThreads() != (aVal == 1))
948       {
949         std::cout << "Warning: unable to switch threads library - no options available\n";
950       }
951     }
952     else if (anIter + 1 < theArgNb
953           && (anArg == "-usetbb"
954            || anArg == "-tousetbb"
955            || anArg == "-tbb"))
956     {
957       const Standard_Integer aVal = Draw::Atoi (theArgVec[++anIter]);
958       OSD_Parallel::SetUseOcctThreads (aVal == 0);
959       if (OSD_Parallel::ToUseOcctThreads() != (aVal == 0))
960       {
961         std::cout << "Warning: unable to switch threads library - no options available\n";
962       }
963     }
964     else
965     {
966       Message::SendFail() << "Syntax error: unknown argument '" << anArg << "'";
967       return 1;
968     }
969   }
970   return 0;
971 }
972
973 //==============================================================================
974 //function : dperf
975 //purpose  :
976 //==============================================================================
977
978 static int dperf (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char** theArgVec)
979 {
980   // reset if argument is provided and it is not '0'
981   int reset = (theArgNb > 1 ? theArgVec[1][0] != '0' && theArgVec[1][0] != '\0' : 0);
982   char buffer[25600];
983   perf_sprint_all_meters (buffer, 25600 - 1, reset);
984   theDI << buffer;
985   return 0;
986 }
987
988 //==============================================================================
989 //function : dsetsignal
990 //purpose  :
991 //==============================================================================
992
993 static int dsetsignal (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char** theArgVec)
994 {
995   OSD_SignalMode aMode = OSD_SignalMode_Set;
996   Standard_Boolean aSetFPE = OSD::ToCatchFloatingSignals();
997   Standard_Integer aStackLen = OSD::SignalStackTraceLength();
998
999   // default for FPE signal is defined by CSF_FPE variable, if set
1000   OSD_Environment aEnv("CSF_FPE");
1001   TCollection_AsciiString aEnvStr = aEnv.Value();
1002   if (!aEnvStr.IsEmpty())
1003   {
1004     aSetFPE = (aEnvStr.Value(1) != '0');
1005   }
1006
1007   // parse arguments
1008   for (Standard_Integer anArgIter = 1; anArgIter < theArgNb; ++anArgIter)
1009   {
1010     TCollection_AsciiString anArg(theArgVec[anArgIter]);
1011     anArg.LowerCase();
1012     if (anArg == "asis")
1013     {
1014       aMode = OSD_SignalMode_AsIs;
1015     }
1016     else if (anArg == "set")
1017     {
1018       aMode = OSD_SignalMode_Set;
1019     }
1020     else if (anArg == "unhandled")
1021     {
1022       aMode = OSD_SignalMode_SetUnhandled;
1023     }
1024     else if (anArg == "unset")
1025     {
1026       aMode = OSD_SignalMode_Unset;
1027     }
1028     else if (anArg == "1" || anArg == "on")
1029     {
1030       aSetFPE = Standard_True;
1031     }
1032     else if (anArg == "0" || anArg == "off")
1033     {
1034       aSetFPE = Standard_False;
1035     }
1036     else if (anArg == "default")
1037     {
1038     }
1039     else if (anArgIter + 1 < theArgNb
1040           && (anArg == "-stracktracelength"
1041            || anArg == "-stracktracelen"
1042            || anArg == "-stracklength"
1043            || anArg == "-stracklen"))
1044     {
1045       aStackLen = Draw::Atoi (theArgVec[++anArgIter]);
1046     }
1047     else
1048     {
1049       Message::SendFail() << "Syntax error: unknown argument '" << anArg << "'";
1050       return 1;
1051     }
1052   }
1053
1054   OSD::SetSignal(aMode, aSetFPE);
1055   OSD::SetSignalStackTraceLength (aStackLen);
1056
1057   // report actual status in the end
1058   const char* aModeStr = 0;
1059   switch (OSD::SignalMode())
1060   {
1061     default:
1062     case OSD_SignalMode_AsIs:         aModeStr = "asis";      break;
1063     case OSD_SignalMode_Set:          aModeStr = "set";       break;
1064     case OSD_SignalMode_SetUnhandled: aModeStr = "unhandled"; break;
1065     case OSD_SignalMode_Unset:        aModeStr = "unset";     break;
1066   }
1067   theDI << "Signal mode: " << aModeStr << "\n"
1068         << "Catch FPE: " << (OSD::ToCatchFloatingSignals() ? "1" : "0") << "\n"
1069         << "Stack Trace Length: " << aStackLen << "\n";
1070   return 0;
1071 }
1072
1073 //==============================================================================
1074 //function : dtracelevel
1075 //purpose  :
1076 //==============================================================================
1077
1078 static int dtracelevel (Draw_Interpretor& theDI,
1079                         Standard_Integer  theArgNb,
1080                         const char**      theArgVec)
1081 {
1082   Message_Gravity aLevel = Message_Info;
1083   if (theArgNb < 1 || theArgNb > 2)
1084   {
1085     Message::SendFail() << "Error: wrong number of arguments! See usage:";
1086     theDI.PrintHelp (theArgVec[0]);
1087     return 1;
1088   }
1089   else if (theArgNb == 2)
1090   {
1091     TCollection_AsciiString aVal (theArgVec[1]);
1092     aVal.LowerCase();
1093     if (aVal == "trace")
1094     {
1095       aLevel = Message_Trace;
1096     }
1097     else if (aVal == "info")
1098     {
1099       aLevel = Message_Info;
1100     }
1101     else if (aVal == "warn"
1102           || aVal == "warning")
1103     {
1104       aLevel = Message_Warning;
1105     }
1106     else if (aVal == "alarm")
1107     {
1108       aLevel = Message_Alarm;
1109     }
1110     else if (aVal == "fail")
1111     {
1112       aLevel = Message_Fail;
1113     }
1114     else
1115     {
1116       Message::SendFail() << "Error: unknown gravity '" << theArgVec[1] << "'";
1117       return 1;
1118     }
1119   }
1120
1121   Handle(Message_Messenger) aMessenger = Message::DefaultMessenger();
1122   if (aMessenger.IsNull())
1123   {
1124     Message::SendFail() << "Error: default messenger is unavailable";
1125     return 1;
1126   }
1127
1128   Message_SequenceOfPrinters& aPrinters = aMessenger->ChangePrinters();
1129   if (aPrinters.Length() < 1)
1130   {
1131     Message::SendFail() << "Error: no printers registered in default Messenger";
1132     return 0;
1133   }
1134
1135   for (Standard_Integer aPrinterIter = 1; aPrinterIter <= aPrinters.Length(); ++aPrinterIter)
1136   {
1137     Handle(Message_Printer)& aPrinter = aPrinters.ChangeValue (aPrinterIter);
1138     if (theArgNb == 1)
1139     {
1140       if (aPrinterIter == 1)
1141       {
1142         aLevel = aPrinter->GetTraceLevel();
1143       }
1144       else if (aLevel == aPrinter->GetTraceLevel())
1145       {
1146         continue;
1147       }
1148
1149       switch (aPrinter->GetTraceLevel())
1150       {
1151         case Message_Trace:   theDI << "trace"; break;
1152         case Message_Info:    theDI << "info";  break;
1153         case Message_Warning: theDI << "warn";  break;
1154         case Message_Alarm:   theDI << "alarm"; break;
1155         case Message_Fail:    theDI << "fail";  break;
1156       }
1157       continue;
1158     }
1159
1160     aPrinter->SetTraceLevel (aLevel);
1161   }
1162
1163   return 0;
1164 }
1165
1166 //==============================================================================
1167 //function : ddebugtraces
1168 //purpose  :
1169 //==============================================================================
1170 static int ddebugtraces (Draw_Interpretor& theDI, Standard_Integer theArgNb, const char** theArgVec)
1171 {
1172   if (theArgNb < 2)
1173   {
1174     theDI << Standard_Failure::DefaultStackTraceLength();
1175     return 0;
1176   }
1177   else if (theArgNb != 2)
1178   {
1179     theDI << "Syntax error: wrong number of arguments";
1180     return 1;
1181   }
1182
1183   Standard_Failure::SetDefaultStackTraceLength (Draw::Atoi (theArgVec[1]));
1184   return 0;
1185 }
1186
1187 //==============================================================================
1188 //function : dputs
1189 //purpose  :
1190 //==============================================================================
1191 static int dputs (Draw_Interpretor& theDI,
1192                   Standard_Integer theArgNb,
1193                   const char** theArgVec)
1194 {
1195   Standard_OStream* aStream = &std::cout;
1196   bool isNoNewline = false, toIntense = false;
1197   Message_ConsoleColor aColor = Message_ConsoleColor_Default;
1198   for (Standard_Integer anArgIter = 1; anArgIter < theArgNb; ++anArgIter)
1199   {
1200     TCollection_AsciiString anArg (theArgVec[anArgIter]);
1201     anArg.LowerCase();
1202     if (anArg == "-nonewline")
1203     {
1204       isNoNewline = true;
1205     }
1206     else if (anArg == "stdcout")
1207     {
1208       aStream = &std::cout;
1209     }
1210     else if (anArg == "stdcerr")
1211     {
1212       aStream = &std::cerr;
1213     }
1214     else if (anArg == "-intense")
1215     {
1216       toIntense = true;
1217     }
1218     else if (anArg == "-black")
1219     {
1220       aColor = Message_ConsoleColor_Black;
1221     }
1222     else if (anArg == "-white")
1223     {
1224       aColor = Message_ConsoleColor_White;
1225     }
1226     else if (anArg == "-red")
1227     {
1228       aColor = Message_ConsoleColor_Red;
1229     }
1230     else if (anArg == "-blue")
1231     {
1232       aColor = Message_ConsoleColor_Blue;
1233     }
1234     else if (anArg == "-green")
1235     {
1236       aColor = Message_ConsoleColor_Green;
1237     }
1238     else if (anArg == "-yellow")
1239     {
1240       aColor = Message_ConsoleColor_Yellow;
1241     }
1242     else if (anArg == "-cyan")
1243     {
1244       aColor = Message_ConsoleColor_Cyan;
1245     }
1246     else if (anArg == "-magenta")
1247     {
1248       aColor = Message_ConsoleColor_Magenta;
1249     }
1250     else if (anArgIter + 1 == theArgNb)
1251     {
1252       if (!theDI.ToColorize())
1253       {
1254         toIntense = false;
1255         aColor = Message_ConsoleColor_Default;
1256       }
1257       if (toIntense || aColor != Message_ConsoleColor_Default)
1258       {
1259         Message_PrinterOStream::SetConsoleTextColor (aStream, aColor, toIntense);
1260       }
1261
1262       *aStream << theArgVec[anArgIter];
1263       if (!isNoNewline)
1264       {
1265         *aStream << std::endl;
1266       }
1267
1268       if (toIntense || aColor != Message_ConsoleColor_Default)
1269       {
1270         Message_PrinterOStream::SetConsoleTextColor (aStream, Message_ConsoleColor_Default, false);
1271       }
1272       return 0;
1273     }
1274     else
1275     {
1276       Message::SendFail() << "Syntax error at '" << anArg << "'";
1277       return 1;
1278     }
1279   }
1280
1281   Message::SendFail() << "Syntax error: wrong number of arguments";
1282   return 1;
1283 }
1284
1285 void Draw::BasicCommands(Draw_Interpretor& theCommands)
1286 {
1287   static Standard_Boolean Done = Standard_False;
1288   if (Done) return;
1289   Done = Standard_True;
1290
1291   std::ios::sync_with_stdio();
1292
1293   const char* g = "DRAW General Commands";
1294
1295   theCommands.Add("batch", "returns 1 in batch mode",
1296                   __FILE__,ifbatch,g);
1297   theCommands.Add("spy","spy [file], Save commands in file. no file close",
1298                   __FILE__,spy,g);
1299   theCommands.Add("wait","wait [time(10)], wait time seconds",
1300                   __FILE__,Draw_wait,g);
1301   theCommands.Add("cpulimit","cpulimit [nbseconds], no args remove limits",
1302                   __FILE__,cpulimit,g);
1303   theCommands.Add("chrono","chrono [name action [action...]] \n  Operates named timer.\n"
1304                            "  Supported actions: reset, start, stop, restart, show, counter [text].\n"
1305                            "  Without arguments enables / disables global timer for all DRAW commands.",
1306                   __FILE__,chronom,g);
1307   theCommands.Add("dchrono","see help of chrono command",
1308                   __FILE__,dchronom,g);
1309   theCommands.Add("mallochook",
1310                   "debug memory allocation/deallocation, w/o args for help",
1311                   __FILE__, mallochook, g);
1312   theCommands.Add ("meminfo",
1313     "meminfo [virt|v] [heap|h] [wset|w] [wsetpeak] [swap] [swappeak] [private]"
1314     " : memory counters for this process",
1315           __FILE__, dmeminfo, g);
1316   theCommands.Add("dperf","dperf [reset] -- show performance counters, reset if argument is provided",
1317                   __FILE__,dperf,g);
1318   theCommands.Add("dsetsignal",
1319             "dsetsignal [{asIs|set|unhandled|unset}=set] [{0|1|default=$CSF_FPE}]"
1320     "\n\t\t:            [-strackTraceLength Length]"
1321     "\n\t\t: Sets OSD signal handler, with FPE option if argument is given."
1322     "\n\t\t:  -strackTraceLength specifies length of stack trace to put into exceptions redirected from signals.",
1323                   __FILE__,dsetsignal,g);
1324
1325   theCommands.Add("dparallel",
1326     "dparallel [-occt {0|1}] [-nbThreads Count] [-nbDefThreads Count]"
1327     "\n\t\t: Manages global parallelization parameters:"
1328     "\n\t\t:   -occt         use OCCT implementation or external library (if available)"
1329     "\n\t\t:   -nbThreads    specify the number of threads in default thread pool"
1330     "\n\t\t:   -nbDefThreads specify the upper limit of threads to be used for default thread pool"
1331     "\n\t\t:                 within single parallelization call (should be <= of overall number of threads),"
1332     "\n\t\t:                 so that nested algorithm can also use this pool",
1333       __FILE__,dparallel,g);
1334
1335   // Logging commands; note that their names are hard-coded in the code
1336   // of Draw_Interpretor, thus should not be changed without update of that code!
1337   theCommands.Add("dlog", "manage logging of commands and output; run without args to get help",
1338                   __FILE__,dlog,g);
1339   theCommands.Add("decho", "switch on / off echo of commands to cout; run without args to get help",
1340                   __FILE__,decho,g);
1341   theCommands.Add("dtracelevel", "dtracelevel [trace|info|warn|alarm|fail]",
1342                   __FILE__, dtracelevel, g);
1343   theCommands.Add("ddebugtraces",
1344     "ddebugtraces nbTraces"
1345     "\n\t\t: Sets the number of lines for the stack trace within Standard_Failure constructor."
1346     "\n\t\t: Intended for debug purposes.",
1347     __FILE__, ddebugtraces, g);
1348
1349   theCommands.Add("dbreak", "raises Tcl exception if user has pressed Control-Break key",
1350                   __FILE__,dbreak,g);
1351   theCommands.Add("dversion", "provides information on OCCT build configuration (version, compiler, OS, C library, etc.)",
1352                   __FILE__,dversion,g);
1353   theCommands.Add("dlocale", "set and / or query locate of C subsystem (function setlocale())",
1354                   __FILE__,dlocale,g);
1355
1356   theCommands.Add("dputs",
1357             "dputs [-intense] [-black|-white|-red|-green|-blue|-yellow|-cyan|-magenta]"
1358     "\n\t\t:       [-nonewline] [stdcout|stdcerr] text"
1359     "\n\t\t: Puts text into console output",
1360                   __FILE__,dputs,g);
1361 }