1 //**************************************************************************************************
2 //                                           PrcBase.cpp                                           *
3 //                                          -------------                                          *
4 // Started     : 2004-01-29                                                                        *
5 // Last Update : 2015-01-06                                                                        *
6 // Copyright   : (C) 2004 by MSWaters                                                              *
7 //**************************************************************************************************
8 
9 //**************************************************************************************************
10 //                                                                                                 *
11 //      This program is free software; you can redistribute it and/or modify it under the          *
12 //      terms of the GNU General Public License as published by the Free Software Foundation;      *
13 //      either version 3 of the License, or (at your option) any later version.                    *
14 //                                                                                                 *
15 //**************************************************************************************************
16 
17 #include "PrcBase.hpp"
18 
19 //**************************************************************************************************
20 // Constructor.
21 
PrcBase(int iFlags)22 PrcBase::PrcBase( int iFlags ) : wxProcess( iFlags )
23 {
24   m_iPid      = -1;            // Clear the process ID number
25   m_iExitCode = 0;             // Clear the process exit code
26   m_ofnLog    = DEF_LOG_FILE;  // Set the default log file path and name
27   m_osErrMsg  = wxT("");       // Clear the error message
28 }
29 
30 //**************************************************************************************************
31 // Destructor.
32 
~PrcBase()33 PrcBase::~PrcBase( )
34 {
35   // Delete the process log file
36   bDelLogFile( );
37 }
38 
39 //**************************************************************************************************
40 // Find a binary using the user's PATH environment variable given the binary name.
41 //
42 // Argument List :
43 //   ofnBin - The binary name without the path
44 //
45 // Return Values :
46 //   true  - Success (path prepended to oFnBinary)
47 //   false - Failure
48 
bFindBinary(wxFileName & rofnBinary)49 bool  PrcBase::bFindBinary( wxFileName & rofnBinary )
50 {
51   wxPathList  opl1;
52   wxFileName  ofn1;
53   wxString    os1, os2, os3;
54 
55   m_osErrMsg = wxT("");
56 
57   // Initial checks
58   if( ! rofnBinary.IsOk( ) ) return( false );
59 
60   // Search environment variable PATH for the first occurrence of the binary
61   opl1.AddEnvList( wxT("PATH") );
62   ofn1 = opl1.FindAbsoluteValidPath( rofnBinary.GetFullName( ) );
63 
64   // Check whether the binary was successfully found
65   if( !ofn1.IsOk( ) || !ofn1.FileExists( ) )
66   {
67     os1 << wxT("Can't find the binary : ") << m_ofnBinary.GetFullName( );
68     wxGetEnv( wxT("PATH"), &os2 );
69     os2.Prepend( wxT("$PATH = \n") );
70 
71     // Set the object error message
72     os3 = os1 + wxT("\n\n") + os2;
73     SetErrMsg( os3 );
74 
75     // If debug mode is enabled send the error message to the console
76     if( g_bDebug )
77     {
78       os3 = os1 + wxT(" (") + os2 + wxT(").");
79       std::cerr << "DEBUG : " << rosStrToLine( os3 ).mb_str( ) << "\n\n";
80     }
81 
82     return( false );
83   }
84 
85   m_ofnBinary = ofn1;
86 
87   return( true );
88 }
89 
90 //**************************************************************************************************
91 // Get the console output after a process has been initiated and record it in the log file.
92 //
93 // Return Values :
94 //   true  - Success
95 //   false - Failure
96 
bLogOutput(void)97 bool  PrcBase::bLogOutput( void )
98 {
99   wxOutputStream * poStdOut;
100   wxInputStream  * poStdIn;
101   wxInputStream  * poStdErr;
102   wxString         os1, os2;
103   wxChar           oc1;
104 
105   // Check that the log file name is valid
106   if( ! m_ofnLog.IsOk( ) )          return( false );
107 
108   // Open the file
109   wxTextFile  oFileLog( m_ofnLog.GetFullPath( ) );
110   if( oFileLog.Exists( ) )
111        { if( ! oFileLog.Open( )   ) return( false ); }
112   else { if( ! oFileLog.Create( ) ) return( false ); }
113 
114   // Clear the file if it contains lines
115   oFileLog.Clear( );
116 
117   // Get pointers to the input streams
118   poStdOut = GetOutputStream( );
119   poStdIn  = GetInputStream( );
120   poStdErr = GetErrorStream( );
121 
122   // Read the console input
123   while( bIsExec( ) || poStdIn->IsOk( ) || poStdErr->IsOk( ) )
124   {
125     // Get a line of data from stdout
126     while( poStdIn->CanRead( ) )
127     {
128       oc1 = poStdIn->GetC( );
129       if( oc1==wxT('\t') || (oc1>31 && oc1!=127) ) os1 << oc1;
130       if( oc1 == wxT('\n') ) { oFileLog.AddLine( os1 ); os1 = wxT(""); }
131     }
132 
133     // Get a line of data from stderr
134     while( poStdErr->CanRead( ) )
135     {
136       oc1 = poStdErr->GetC( );
137       if( oc1==wxT('\t') || (oc1>31 && oc1!=127) ) os2 << oc1;
138       if( oc1 == wxT('\n') ) { oFileLog.AddLine( os2 ); os2 = wxT(""); }
139     }
140 
141     wxYield( ); // Yield CPU to other processes
142   }
143 
144   // Delete the stream objects
145   delete poStdOut;
146   delete poStdIn;
147   delete poStdErr;
148   SetPipeStreams( NULL, NULL, NULL );
149 
150   if( ! os1.IsEmpty( ) ) oFileLog.AddLine( os1 );
151   if( ! os2.IsEmpty( ) ) oFileLog.AddLine( os2 );
152 
153   oFileLog.Write( ); // Save the changes to disk
154   oFileLog.Close( ); // Close the log file
155 
156   return( true );
157 }
158 
159 //**************************************************************************************************
160 // Does the binary exist?
161 //
162 // Return Values :
163 //   true  - The binary does    exist
164 //   false - The binary doesn't exist
165 
bBinExists(void)166 bool  PrcBase::bBinExists( void )
167 {
168   if( !m_ofnBinary.IsOk( ) || !m_ofnBinary.FileExists( ) )
169     return( false );
170 
171   return( true );
172 }
173 
174 //**************************************************************************************************
175 // Set the file name of the binary to be executed.
176 //
177 // Argument List :
178 //   rosFName - A string containing the binary file name
179 //
180 // Return Values :
181 //   true  - Success
182 //   false - Failure
183 
bSetBinary(const wxString & rosFName)184 bool  PrcBase::bSetBinary( const wxString & rosFName )
185 {
186   if( rosFName.IsEmpty( ) )          return( false );
187 
188   m_ofnBinary = rosFName;
189 
190   if( ! bFindBinary( m_ofnBinary ) ) return( false );
191 
192   return( true );
193 }
194 
195 //**************************************************************************************************
196 // Set the argument list to be appended to the binary name.
197 //
198 // Argument List :
199 //   rosArgLst - A string containing the argument list
200 //
201 // Return Values :
202 //   true  - Success
203 //   false - Failure
204 
bSetArgLst(const wxString & rosArgLst)205 bool  PrcBase::bSetArgLst( const wxString & rosArgLst )
206 {
207   if( rosArgLst.IsEmpty( ) ) return( false );
208 
209   m_osArgLst = rosArgLst;
210 
211   return( true );
212 }
213 
214 //**************************************************************************************************
215 // Set the process log file name.
216 // (If set all process output can be captured to this file.)
217 //
218 // Argument List :
219 //   rosFName - A string containing the full path and file name
220 //
221 // Return Values :
222 //   true  - Success
223 //   false - Failure
224 
bSetLogFile(const wxString & rosFName)225 bool  PrcBase::bSetLogFile( const wxString & rosFName )
226 {
227   wxFileName  ofn1;
228 
229   ofn1 = rosFName;
230 
231   if( ! ofn1.IsOk( ) ) return( false );
232 
233   if( ofn1.GetPath( ).IsEmpty( ) ) ofn1.SetPath( wxT(".") );
234 
235   m_ofnLog = ofn1;
236 
237   return( true );
238 }
239 
240 //**************************************************************************************************
241 // Execute a process asynchronously.
242 // (Ie. Return as soon as the process has been launched).
243 //
244 // Return Values :
245 //   true  - Success
246 //   false - Failure
247 
bExecAsync(void)248 bool  PrcBase::bExecAsync( void )
249 {
250   wxString  os1, os2;
251 
252   // Only execute simulation if it isn't already running
253   if( bIsExec( ) )      return( false );
254 
255   // Clear error attributes
256   m_osErrMsg = wxT("");
257 
258   // Check that the binary exists
259   if( ! bBinExists( ) )
260     if( ! bFindBinary( m_ofnBinary ) )
261       return( false );
262 
263   // Construct the command line to be executed
264   os1 = roGetBinary( ).GetFullPath( );
265   if( ! rosGetArgLst( ).IsEmpty( ) )
266     os1 << wxT(' ') << rosGetArgLst( );
267 
268   // Attempt to execute the simulation
269   m_iPid = (int) wxExecute( os1, wxEXEC_ASYNC, this );
270   if( m_iPid <= 0 )
271   {
272     os2.Empty( );
273     os2 << wxT("Couldn't start process : ") << os1;
274     SetErrMsg( os2 );
275     return( false );
276   }
277 
278   return( true );
279 }
280 
281 //**************************************************************************************************
282 // Execute a process synchronously.
283 // (Ie. wait for the process to terminate before returning).
284 //
285 // Return Values :
286 //   true  - Success
287 //   false - Failure
288 
bExecSync(void)289 bool  PrcBase::bExecSync( void )
290 {
291   // Not yet implemented ??? 12/03/2010
292 
293   return( false );
294 
295 //  long  liRtn;
296 
297 //  wxEnableTopLevelWindows( false );
298 //  liRtn = wxExecute( rosCmd, wxEXEC_SYNC );
299 //  wxEnableTopLevelWindows( true );
300 //  if( liRtn != 0 ) return( false );
301 
302 //  return( true );
303 
304 // This is how I'd like to do it but it doesn't work 20/11/2003 ???
305 //  wxEnableTopLevelWindows( false );
306 //  int iRtn = (int) wxExecute( osCmd, wxEXEC_SYNC );
307 //  wxEnableTopLevelWindows( true );
308 
309 //  wxProcess  oProcess( wxPROCESS_DEFAULT );
310 //  iPid = (int) wxExecute( osCmd, wxEXEC_SYNC );
311 
312 //  if( iPid == 0 )
313 //  { // Error gnetlist could not be executed
314 //    cerr << "The command:\n   " << osCmd << "\ncould not be executed.";
315 //    return( false );
316 //  }
317 
318 //  for( ui1=0; ui1<300; ui1++ )
319 //  { // Wait up to 30 seconds for the process to complete
320 //cerr << iPid << '\n';
321 //    wxUsleep( 100 ); // Sleep for 100msec
322 //    if( ! oProcess.Exists( iPid ) ) break;
323 //  }
324 //  if( oProcess.Exists( iPid ) )
325 //  { // Error gnetlist had to be terminated prematurely
326 //    oProcess.Kill( iPid );
327 //    cerr << "The command:\n   " << osCmd << "\ntook more than 30 sec. to "
328 //         << "execute and so was terminated prematurely.";
329 //    return( false );
330 //  }
331 }
332 
333 //**************************************************************************************************
334 // Stop the simulation currently in progress.
335 //
336 // Return Values :
337 //   true  - Success
338 //   false - Failure
339 
bKill(void)340 bool  PrcBase::bKill( void )
341 {
342   if( ! bIsExec( ) )       return( true );
343   if( ! Exists( m_iPid ) ) return( true );
344 
345   if( Kill( m_iPid, wxSIGTERM ) != wxKILL_OK ) return( false );
346 
347   m_iPid      = -1;
348   m_iExitCode = 0;
349 
350   return( true );
351 }
352 
353 //**************************************************************************************************
354 // Remove the log file.
355 //
356 // Return Values :
357 //   Success - true
358 //   Failure - false
359 
bDelLogFile(void)360 bool  PrcBase::bDelLogFile( void )
361 {
362   if( m_ofnLog.GetFullPath( ).IsEmpty( ) )          return( true );
363 
364   if( ! ::wxFileExists( m_ofnLog.GetFullPath( ) ) ) return( true );
365 
366   return( ::wxRemoveFile( m_ofnLog.GetFullPath( ) ) );
367 }
368 
369 //**************************************************************************************************
370 // Collect the output from the simulator and print to a text control.
371 //
372 // Argument List :
373 //   roTxtCtl - The text control to contain the simulator output
374 
Print(TextCtrl & roTxtCtl)375 void  PrcBase::Print( TextCtrl & roTxtCtl )
376 {
377   roTxtCtl.bClear( );    // Clear the text control
378 
379   PrintCmd( roTxtCtl );  // Print the process command
380 
381   PrintRsp( roTxtCtl );  // Print the process command response
382 }
383 
384 //**************************************************************************************************
385 // Print the command envoking the process to a text control.
386 //
387 // Argument List :
388 //   roTxtCtl - The text control to contain process input
389 
PrintCmd(TextCtrl & roTxtCtl)390 void  PrcBase::PrintCmd( TextCtrl & roTxtCtl )
391 {
392   wxString  os1;
393 
394   // Print the process command
395   os1.Empty( );
396   os1 << wxT("                    ")
397       << wxT("*************** PROCESS COMMAND ***************");
398   roTxtCtl.bAppendLine( os1 );
399   os1.Empty( );
400   roTxtCtl.bAppendLine( os1 );
401   os1 << roGetBinary( ).GetFullPath( );
402   if( ! rosGetArgLst( ).IsEmpty( ) ) os1 << wxT(' ') << rosGetArgLst( );
403   roTxtCtl.bAppendLine( os1 );
404   os1.Empty( );
405   roTxtCtl.bAppendLine( os1 );
406 }
407 
408 //**************************************************************************************************
409 // Collect the output from the process and print it to a text control.
410 //
411 // Argument List :
412 //   roTxtCtl - The text control to contain the simulator output
413 
PrintRsp(TextCtrl & roTxtCtl)414 void  PrcBase::PrintRsp( TextCtrl & roTxtCtl )
415 {
416   wxString  os1;
417 
418   // Print the process command response
419   if( ! m_ofnLog.GetFullPath( ).IsEmpty( ) )
420   {
421     os1.Empty( );
422     os1 << wxT("                    ")
423         << wxT("*************** PROCESS RESPONSE **************");
424     roTxtCtl.bAppendLine( os1 );
425     os1.Empty( );
426     roTxtCtl.bAppendLine( os1 );
427     if( m_ofnLog.IsOk( ) && m_ofnLog.FileExists( ) )
428       roTxtCtl.bAppendFile( m_ofnLog.GetFullPath( ) );
429     else
430     {
431       os1 << wxT("Couldn't load the file containing the process ouput : ")
432           << m_ofnLog.GetFullPath( );
433       roTxtCtl.bAppendLine( os1 );
434     }
435   }
436 }
437 
438 //**************************************************************************************************
439 // Call-back function called when the process terminates.
440 // (WARNING : Be careful of this function definition, if it is incorrect the
441 //            application can segment fault.)
442 //
443 // Argument List :
444 //   iPid    - The PID of the process which just terminated
445 //   iStatus - The exit code of the process
446 
OnTerminate(int iPid,int iStatus)447 void  PrcBase::OnTerminate( int iPid, int iStatus )
448 {
449   if( iPid == m_iPid )
450   {
451     m_iPid      = -1;
452     m_iExitCode = iStatus;
453   }
454 }
455 
456 //**************************************************************************************************
457