return 0;
}
+static Standard_Integer dlog(Draw_Interpretor& di, Standard_Integer n, const char** a)
+{
+ if (n != 2 && n != 3)
+ {
+ cout << "Enable or disable logging: " << a[0] << " {on|off}" << endl;
+ cout << "Reset log: " << a[0] << " reset" << endl;
+ cout << "Get log content: " << a[0] << " get" << endl;
+ return 1;
+ }
+
+ if (! strcmp (a[1], "on") && n == 2)
+ {
+ di.SetDoLog (Standard_True);
+// di.Log() << "dlog on" << endl; // for symmetry
+ }
+ else if (! strcmp (a[1], "off") && n == 2)
+ {
+ di.SetDoLog (Standard_False);
+ }
+ else if (! strcmp (a[1], "reset") && n == 2)
+ {
+ di.Log().str("");
+ }
+ else if (! strcmp (a[1], "get") && n == 2)
+ {
+ di << di.Log().str().c_str();
+ }
+ else if (! strcmp (a[1], "add") && n == 3)
+ {
+ di.Log() << a[2] << "\n";
+ }
+ else {
+ cout << "Unrecognized option(s): " << a[1] << endl;
+ return 1;
+ }
+ return 0;
+}
+
+static Standard_Integer decho(Draw_Interpretor& di, Standard_Integer n, const char** a)
+{
+ if (n != 2)
+ {
+ cout << "Enable or disable echoing: " << a[0] << " {on|off}" << endl;
+ return 1;
+ }
+
+ if (! strcmp (a[1], "on"))
+ {
+ di.SetDoEcho (Standard_True);
+ }
+ else if (! strcmp (a[1], "off"))
+ {
+ di.SetDoEcho (Standard_False);
+ }
+ else {
+ cout << "Unrecognized option: " << a[1] << endl;
+ return 1;
+ }
+ return 0;
+}
+
//=======================================================================
//function : wait
//purpose :
if (Done) return;
Done = Standard_True;
+ ios::sync_with_stdio();
+
const char* g = "DRAW General Commands";
theCommands.Add("batch", "returns 1 in batch mode",
"meminfo [virt|v] [wset|w] [wsetpeak] [swap] [swappeak] [private]"
" : memory counters for this process",
__FILE__, dmeminfo, g);
+
+ // Logging commands; note that their names are hard-coded in the code
+ // of Draw_Interpretor, thus should not be changed without update of that code!
+ theCommands.Add("dlog", "manage logging of commands and output; run without args to get help",
+ __FILE__,dlog,g);
+ theCommands.Add("decho", "switch on / off echo of commands to cout; run without args to get help",
+ __FILE__,decho,g);
}
#include <TCollection_ExtendedString.hxx>
#include <string.h>
-
#include <tcl.h>
+// for capturing of cout and cerr (dup(), dup2())
+#ifdef _MSC_VER
+#include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if ! defined(STDOUT_FILENO)
+#define STDOUT_FILENO fileno(stdout)
+#endif
+#if ! defined(STDERR_FILENO)
+#define STDERR_FILENO fileno(stderr)
+#endif
+
#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 1)))
#define TCL_USES_UTF8
#endif
Draw_Interpretor* i;
};
+// logging helpers
+namespace {
+ void dumpArgs (Standard_OStream& os, int argc, const char *argv[])
+ {
+ for (int i=0; i < argc; i++)
+ os << argv[i] << " ";
+ os << endl;
+ }
+
+ void flush_standard_streams ()
+ {
+ fflush (stderr);
+ fflush (stdout);
+ cerr << flush;
+ cout << flush;
+ }
+
+ FILE* capture_start (int std_fd, int *save_fd)
+ {
+ (*save_fd) = 0;
+
+ // open temporary files
+ FILE * aTmpFile = tmpfile();
+ int fd_tmp = fileno(aTmpFile);
+
+ if (fd_tmp <0)
+ {
+ cerr << "Error: cannot create temporary file for capturing console output" << endl;
+ fclose (aTmpFile);
+ return NULL;
+ }
+
+ // remember current file descriptors of standard stream, and replace it by temporary
+ (*save_fd) = dup(std_fd);
+ dup2(fd_tmp, std_fd);
+ return aTmpFile;
+ }
+
+ void capture_end (FILE* tmp_file, int std_fd, int save_fd, Standard_OStream &log, Standard_Boolean doEcho)
+ {
+ // restore normal descriptors of console stream
+ dup2 (save_fd, std_fd);
+ close(save_fd);
+
+ // extract all output and copy it to log and optionally to cout
+ const int BUFSIZE = 2048;
+ char buf[BUFSIZE];
+ rewind(tmp_file);
+ while (fgets (buf, BUFSIZE, tmp_file) != NULL)
+ {
+ log << buf;
+ if (doEcho)
+ cout << buf;
+ }
+
+ // close temporary file
+ fclose (tmp_file);
+ }
+};
+
// MKV 29.03.05
#if ((TCL_MAJOR_VERSION > 8) || ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4))) && !defined(USE_NON_CONST)
static Standard_Integer CommandCmd
static Standard_Integer code;
code = TCL_OK;
CData* C = (CData*) clientData;
+ Draw_Interpretor& di = *(C->i);
+
+ // log command execution, except commands manipulating log itself and echo
+ Standard_Boolean isLogManipulation = (strcmp (argv[0], "dlog") == 0 ||
+ strcmp (argv[0], "decho") == 0);
+ Standard_Boolean doLog = (di.GetDoLog() && ! isLogManipulation);
+ Standard_Boolean doEcho = (di.GetDoEcho() && ! isLogManipulation);
+ if (doLog)
+ dumpArgs (di.Log(), argc, argv);
+ if (doEcho)
+ dumpArgs (cout, argc, argv);
+
+ // flush cerr and cout
+ flush_standard_streams();
+
+ // capture cout and cerr to log
+ FILE * aFile_err = NULL;
+ FILE * aFile_out = NULL;
+ int fd_err_save = 0;
+ int fd_out_save = 0;
+ if (doLog)
+ {
+ aFile_out = capture_start (STDOUT_FILENO, &fd_out_save);
+ aFile_err = capture_start (STDERR_FILENO, &fd_err_save);
+ }
+ // run command
try {
OCC_CATCH_SIGNALS
// OCC63: Convert strings from UTF-8 to local encoding, normally expected by OCC commands
TclUTFToLocalStringSentry anArgs ( argc, (const char**)argv );
- Draw_Interpretor& di = *(C->i);
Standard_Integer fres = C->f ( di, argc, anArgs.GetArgv() );
if (fres != 0)
code = TCL_ERROR;
#endif
code = TCL_ERROR;
}
-
+
+ // flush streams
+ flush_standard_streams();
+
+ // end capturing cout and cerr
+ if (doLog)
+ {
+ capture_end (aFile_err, STDERR_FILENO, fd_err_save, di.Log(), doEcho);
+ capture_end (aFile_out, STDOUT_FILENO, fd_out_save, di.Log(), doEcho);
+ }
+
+ // log command result
+ const char* aResultStr = NULL;
+ if (doLog)
+ {
+ aResultStr = Tcl_GetStringResult (interp);
+ if (aResultStr != 0 && aResultStr[0] != '\0' )
+ di.Log() << Tcl_GetStringResult (interp) << endl;
+ }
+ if (doEcho)
+ {
+ if (aResultStr == NULL)
+ aResultStr = Tcl_GetStringResult (interp);
+ if (aResultStr != 0 && aResultStr[0] != '\0' )
+ cout << Tcl_GetStringResult (interp) << endl;
+ }
+
return code;
}
//=======================================================================
Draw_Interpretor::Draw_Interpretor() :
- isAllocated(Standard_False)
+ isAllocated(Standard_False), myDoLog(Standard_False), myDoEcho(Standard_False)
{
// The tcl interpreter is not created immediately as it is kept
// by a global variable and created and deleted before the main().
Draw_Interpretor::Draw_Interpretor(const Draw_PInterp& p) :
isAllocated(Standard_False),
- myInterp(p)
+ myInterp(p),
+ myDoLog(Standard_False),
+ myDoEcho(Standard_False)
{
}
isAllocated = Standard_False;
myInterp = PIntrp;
}
+
+//=======================================================================
+//function : Logging
+//purpose :
+//=======================================================================
+
+void Draw_Interpretor::SetDoLog (Standard_Boolean doLog)
+{
+ myDoLog = doLog;
+}
+
+void Draw_Interpretor::SetDoEcho (Standard_Boolean doEcho)
+{
+ myDoEcho = doEcho;
+}
+
+Standard_Boolean Draw_Interpretor::GetDoLog () const
+{
+ return myDoLog;
+}
+
+Standard_Boolean Draw_Interpretor::GetDoEcho () const
+{
+ return myDoEcho;
+}
+
+Standard_SStream& Draw_Interpretor::Log ()
+{
+ return myLog;
+}
\ No newline at end of file