KiCad PCB EDA Suite
common.cpp File Reference
#include <eda_base_frame.h>
#include <kiplatform/app.h>
#include <project.h>
#include <common.h>
#include <reporter.h>
#include <macros.h>
#include <mutex>
#include <wx/config.h>
#include <wx/log.h>
#include <wx/msgdlg.h>
#include <wx/process.h>
#include <wx/stdpaths.h>
#include <wx/url.h>
#include <wx/utils.h>

Go to the source code of this file.

Enumerations

enum  Bracket { Bracket_None, Bracket_Normal = ')', Bracket_Curly = '}', Bracket_Max }
 

Functions

int ProcessExecute (const wxString &aCommandLine, int aFlags, wxProcess *callback)
 Run a command in a child process. More...
 
wxString ExpandTextVars (const wxString &aSource, const PROJECT *aProject)
 
wxString ExpandTextVars (const wxString &aSource, const std::function< bool(wxString *)> *aLocalResolver, const std::function< bool(wxString *)> *aFallbackResolver, const PROJECT *aProject)
 Expand '${var-name}' templates in text. More...
 
wxString KIwxExpandEnvVars (const wxString &str, const PROJECT *aProject)
 
const wxString ExpandEnvVarSubstitutions (const wxString &aString, PROJECT *aProject)
 Replace any environment variable & text variable references with their values. More...
 
const wxString ResolveUriByEnvVars (const wxString &aUri, PROJECT *aProject)
 Replace any environment and/or text variables in file-path uris (leaving network-path URIs alone). More...
 
bool EnsureFileDirectoryExists (wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
 Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist. More...
 
bool matchWild (const char *pat, const char *text, bool dot_special)
 Performance enhancements to file and directory operations. More...
 
long long TimestampDir (const wxString &aDirPath, const wxString &aFilespec)
 A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function private to src/common/filename.cpp. More...
 
bool WarnUserIfOperatingSystemUnsupported ()
 Checks if the operating system is explicitly unsupported and displays a disclaimer message box. More...
 

Enumeration Type Documentation

◆ Bracket

enum Bracket
Enumerator
Bracket_None 
Bracket_Normal 
Bracket_Curly 
Bracket_Max 

Definition at line 52 of file common.cpp.

53 {
55  Bracket_Normal = ')',
56  Bracket_Curly = '}',
57 #ifdef __WINDOWS__
58  Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
59 #endif
61 };

Function Documentation

◆ EnsureFileDirectoryExists()

bool EnsureFileDirectoryExists ( wxFileName *  aTargetFullFileName,
const wxString &  aBaseFilename,
REPORTER aReporter = nullptr 
)

Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.

Parameters
aTargetFullFileNamethe wxFileName containing the full path and file name to modify. The path may be absolute or relative to aBaseFilename .
aBaseFilenamea full filename. Only its path is used to set the aTargetFullFileName path.
aReportera point to a REPORTER object use to show messages (can be NULL)
Returns
true if aOutputDir already exists or was successfully created.

Definition at line 307 of file common.cpp.

310 {
311  wxString msg;
312  wxString baseFilePath = wxFileName( aBaseFilename ).GetPath();
313 
314  // make aTargetFullFileName path, which is relative to aBaseFilename path (if it is not
315  // already an absolute path) absolute:
316  if( !aTargetFullFileName->MakeAbsolute( baseFilePath ) )
317  {
318  if( aReporter )
319  {
320  msg.Printf( _( "Cannot make path '%s' absolute with respect to '%s'." ),
321  aTargetFullFileName->GetPath(),
322  baseFilePath );
323  aReporter->Report( msg, RPT_SEVERITY_ERROR );
324  }
325 
326  return false;
327  }
328 
329  // Ensure the path of aTargetFullFileName exists, and create it if needed:
330  wxString outputPath( aTargetFullFileName->GetPath() );
331 
332  if( !wxFileName::DirExists( outputPath ) )
333  {
334  // Make every directory provided when the provided path doesn't exist
335  if( wxFileName::Mkdir( outputPath, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
336  {
337  if( aReporter )
338  {
339  msg.Printf( _( "Output directory '%s' created.\n" ), outputPath );
340  aReporter->Report( msg, RPT_SEVERITY_INFO );
341  return true;
342  }
343  }
344  else
345  {
346  if( aReporter )
347  {
348  msg.Printf( _( "Cannot create output directory '%s'.\n" ), outputPath );
349  aReporter->Report( msg, RPT_SEVERITY_ERROR );
350  }
351 
352  return false;
353  }
354  }
355 
356  return true;
357 }
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
#define _(s)

References _, REPORTER::Report(), RPT_SEVERITY_ERROR, and RPT_SEVERITY_INFO.

Referenced by DIALOG_GEN_FOOTPRINT_POSITION::CreateAsciiFiles(), DIALOG_GEN_FOOTPRINT_POSITION::CreateGerberFiles(), DIALOG_PLOT_SCHEMATIC::createPlotFileName(), DIALOG_EXPORT_SVG::ExportSVGFile(), DIALOG_GENDRILL::GenDrillAndMapFiles(), PLOT_CONTROLLER::OpenPlotfile(), and DIALOG_PLOT::Plot().

◆ ExpandEnvVarSubstitutions()

const wxString ExpandEnvVarSubstitutions ( const wxString &  aString,
PROJECT aProject 
)

Replace any environment variable & text variable references with their values.

Parameters
aStringa string containing (perhaps) references to env var
Returns
the expanded environment variable.

Definition at line 279 of file common.cpp.

280 {
281  // wxGetenv( wchar_t* ) is not re-entrant on linux.
282  // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),
283  static std::mutex getenv_mutex;
284 
285  std::lock_guard<std::mutex> lock( getenv_mutex );
286 
287  // We reserve the right to do this another way, by providing our own member function.
288  return KIwxExpandEnvVars( aString, aProject );
289 }
wxString KIwxExpandEnvVars(const wxString &str, const PROJECT *aProject)
Definition: common.cpp:128

References KIwxExpandEnvVars().

Referenced by FILENAME_RESOLVER::addPath(), FILENAME_RESOLVER::checkEnvVarPath(), S3D_PLUGIN_MANAGER::checkPluginName(), S3D_PLUGIN_MANAGER::checkPluginPath(), DIALOG_GEN_FOOTPRINT_POSITION::CreateAsciiFiles(), DIALOG_GEN_FOOTPRINT_POSITION::CreateGerberFiles(), FILENAME_RESOLVER::createPathList(), DIALOG_EXPORT_SVG::ExportSVGFile(), DIALOG_GENDRILL::GenDrillAndMapFiles(), LIB_TABLE_ROW::GetFullURI(), DIALOG_PLOT_SCHEMATIC::getOutputPath(), DS_DATA_MODEL::MakeFullFileName(), TEXT_BUTTON_FILE_BROWSER::OnButtonClick(), PANEL_SYM_LIB_TABLE::onConvertLegacyLibraries(), DIALOG_GENDRILL::OnGenReportFile(), DIALOG_PLOT::OnOutputDirectoryBrowseClicked(), DIALOG_EXPORT_SVG::OnOutputDirectoryBrowseClicked(), DIALOG_GENDRILL::OnOutputDirectoryBrowseClicked(), DIALOG_GEN_FOOTPRINT_POSITION::OnOutputDirectoryBrowseClicked(), DIALOG_PLOT_SCHEMATIC::OnOutputDirectoryBrowseClicked(), DIALOG_PLOT::Plot(), FILENAME_RESOLVER::ResolvePath(), ResolveUriByEnvVars(), FILENAME_RESOLVER::Set3DConfigDir(), S3D_CACHE::Set3DConfigDir(), FILENAME_RESOLVER::SetProject(), FILENAME_RESOLVER::ShortenPath(), and ERC_TESTER::TestTextVars().

◆ ExpandTextVars() [1/2]

wxString ExpandTextVars ( const wxString &  aSource,
const PROJECT aProject 
)

Definition at line 64 of file common.cpp.

65 {
66  return ExpandTextVars( aSource, nullptr, nullptr, aProject );
67 }
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:64

References ExpandTextVars().

Referenced by AddGerberX2Header(), DS_DRAW_ITEM_LIST::BuildFullText(), ExpandTextVars(), PCB_TEXT::GetShownText(), SCH_FIELD::GetShownText(), SCH_TEXT::GetShownText(), FP_TEXT::GetShownText(), NETLIST_EXPORTER_XML::makeDesignHeader(), ResolveUriByEnvVars(), and TITLE_BLOCK::TextVarResolver().

◆ ExpandTextVars() [2/2]

wxString ExpandTextVars ( const wxString &  aSource,
const std::function< bool(wxString *)> *  aLocalResolver,
const std::function< bool(wxString *)> *  aFallbackResolver,
const PROJECT aProject 
)

Expand '${var-name}' templates in text.

The LocalResolver is given first crack at it, after which the PROJECT's resolver is called.

Definition at line 70 of file common.cpp.

74 {
75  wxString newbuf;
76  size_t sourceLen = aSource.length();
77 
78  newbuf.Alloc( sourceLen ); // best guess (improves performance)
79 
80  for( size_t i = 0; i < sourceLen; ++i )
81  {
82  if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
83  {
84  wxString token;
85 
86  for( i = i + 2; i < sourceLen; ++i )
87  {
88  if( aSource[i] == '}' )
89  break;
90  else
91  token.append( aSource[i] );
92  }
93 
94  if( token.IsEmpty() )
95  continue;
96 
97  if( aLocalResolver && (*aLocalResolver)( &token ) )
98  {
99  newbuf.append( token );
100  }
101  else if( aProject && aProject->TextVarResolver( &token ) )
102  {
103  newbuf.append( token );
104  }
105  else if( aFallbackResolver && (*aFallbackResolver)( &token ) )
106  {
107  newbuf.append( token );
108  }
109  else
110  {
111  // Token not resolved: leave the reference unchanged
112  newbuf.append( "${" + token + "}" );
113  }
114  }
115  else
116  {
117  newbuf.append( aSource[i] );
118  }
119  }
120 
121  return newbuf;
122 }
virtual bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:67

References PROJECT::TextVarResolver().

◆ KIwxExpandEnvVars()

wxString KIwxExpandEnvVars ( const wxString &  str,
const PROJECT aProject 
)

Definition at line 128 of file common.cpp.

129 {
130  size_t strlen = str.length();
131 
132  wxString strResult;
133  strResult.Alloc( strlen ); // best guess (improves performance)
134 
135  for( size_t n = 0; n < strlen; n++ )
136  {
137  wxUniChar str_n = str[n];
138 
139  switch( str_n.GetValue() )
140  {
141 #ifdef __WINDOWS__
142  case wxT( '%' ):
143 #endif // __WINDOWS__
144  case wxT( '$' ):
145  {
146  Bracket bracket;
147 #ifdef __WINDOWS__
148  if( str_n == wxT( '%' ) )
149  bracket = Bracket_Windows;
150  else
151 #endif // __WINDOWS__
152  if( n == strlen - 1 )
153  {
154  bracket = Bracket_None;
155  }
156  else
157  {
158  switch( str[n + 1].GetValue() )
159  {
160  case wxT( '(' ):
161  bracket = Bracket_Normal;
162  str_n = str[++n]; // skip the bracket
163  break;
164 
165  case wxT( '{' ):
166  bracket = Bracket_Curly;
167  str_n = str[++n]; // skip the bracket
168  break;
169 
170  default:
171  bracket = Bracket_None;
172  }
173  }
174 
175  size_t m = n + 1;
176 
177  if( m >= strlen )
178  break;
179 
180  wxUniChar str_m = str[m];
181 
182  while( wxIsalnum( str_m ) || str_m == wxT( '_' ) || str_m == wxT( ':' ) )
183  {
184  if( ++m == strlen )
185  {
186  str_m = 0;
187  break;
188  }
189 
190  str_m = str[m];
191  }
192 
193  wxString strVarName( str.c_str() + n + 1, m - n - 1 );
194 
195  // NB: use wxGetEnv instead of wxGetenv as otherwise variables
196  // set through wxSetEnv may not be read correctly!
197  bool expanded = false;
198  wxString tmp = strVarName;
199 
200  if( aProject && aProject->TextVarResolver( &tmp ) )
201  {
202  strResult += tmp;
203  expanded = true;
204  }
205  else if( wxGetEnv( strVarName, &tmp ) )
206  {
207  strResult += tmp;
208  expanded = true;
209  }
210  else
211  {
212  // variable doesn't exist => don't change anything
213 #ifdef __WINDOWS__
214  if ( bracket != Bracket_Windows )
215 #endif
216  if ( bracket != Bracket_None )
217  strResult << str[n - 1];
218 
219  strResult << str_n << strVarName;
220  }
221 
222  // check the closing bracket
223  if( bracket != Bracket_None )
224  {
225  if( m == strlen || str_m != (wxChar)bracket )
226  {
227  // under MSW it's common to have '%' characters in the registry
228  // and it's annoying to have warnings about them each time, so
229  // ignore them silently if they are not used for env vars
230  //
231  // under Unix, OTOH, this warning could be useful for the user to
232  // understand why isn't the variable expanded as intended
233 #ifndef __WINDOWS__
234  wxLogWarning( _( "Environment variables expansion failed: missing '%c' "
235  "at position %u in '%s'." ),
236  (char)bracket, (unsigned int) (m + 1), str.c_str() );
237 #endif // __WINDOWS__
238  }
239  else
240  {
241  // skip closing bracket unless the variables wasn't expanded
242  if( !expanded )
243  strResult << (wxChar)bracket;
244 
245  m++;
246  }
247  }
248 
249  n = m - 1; // skip variable name
250  str_n = str[n];
251  }
252  break;
253 
254  case wxT( '\\' ):
255  // backslash can be used to suppress special meaning of % and $
256  if( n < strlen - 1 && (str[n + 1] == wxT( '%' ) || str[n + 1] == wxT( '$' )) )
257  {
258  str_n = str[++n];
259  strResult += str_n;
260 
261  break;
262  }
264 
265  default:
266  strResult += str_n;
267  }
268  }
269 
270 #ifndef __WINDOWS__
271  if( strResult.StartsWith( "~" ) )
272  strResult.Replace( "~", wxGetHomeDir(), false );
273 #endif // __WINDOWS__
274 
275  return strResult;
276 }
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
Bracket
Definition: common.cpp:52
virtual bool TextVarResolver(wxString *aToken) const
Definition: project.cpp:67
#define _(s)

References _, Bracket_Curly, Bracket_None, Bracket_Normal, KI_FALLTHROUGH, and PROJECT::TextVarResolver().

Referenced by ExpandEnvVarSubstitutions().

◆ matchWild()

bool matchWild ( const char *  pat,
const char *  text,
bool  dot_special 
)

Performance enhancements to file and directory operations.

Note: while it's annoying to have to make copies of wxWidgets stuff and then add platform-specific performance optimizations, the following routines offer SIGNIFICANT performance benefits.A copy of wxMatchWild(), which wxWidgets attributes to Douglas A. Lewis dalew.nosp@m.is@c.nosp@m.s.Buf.nosp@m.falo.nosp@m..EDU and ircII's reg.c.

This version is modified to skip any encoding conversions (for performance).

Definition at line 374 of file common.cpp.

375 {
376  if( !*text )
377  {
378  /* Match if both are empty. */
379  return !*pat;
380  }
381 
382  const char *m = pat,
383  *n = text,
384  *ma = NULL,
385  *na = NULL;
386  int just = 0,
387  acount = 0,
388  count = 0;
389 
390  if( dot_special && (*n == '.') )
391  {
392  /* Never match so that hidden Unix files
393  * are never found. */
394  return false;
395  }
396 
397  for(;;)
398  {
399  if( *m == '*' )
400  {
401  ma = ++m;
402  na = n;
403  just = 1;
404  acount = count;
405  }
406  else if( *m == '?' )
407  {
408  m++;
409 
410  if( !*n++ )
411  return false;
412  }
413  else
414  {
415  if( *m == '\\' )
416  {
417  m++;
418 
419  /* Quoting "nothing" is a bad thing */
420  if( !*m )
421  return false;
422  }
423  if( !*m )
424  {
425  /*
426  * If we are out of both strings or we just
427  * saw a wildcard, then we can say we have a
428  * match
429  */
430  if( !*n )
431  return true;
432 
433  if( just )
434  return true;
435 
436  just = 0;
437  goto not_matched;
438  }
439 
440  /*
441  * We could check for *n == NULL at this point, but
442  * since it's more common to have a character there,
443  * check to see if they match first (m and n) and
444  * then if they don't match, THEN we can check for
445  * the NULL of n
446  */
447  just = 0;
448 
449  if( *m == *n )
450  {
451  m++;
452  count++;
453  n++;
454  }
455  else
456  {
457  not_matched:
458 
459  /*
460  * If there are no more characters in the
461  * string, but we still need to find another
462  * character (*m != NULL), then it will be
463  * impossible to match it
464  */
465  if( !*n )
466  return false;
467 
468  if( ma )
469  {
470  m = ma;
471  n = ++na;
472  count = acount;
473  }
474  else
475  return false;
476  }
477  }
478  }
479 }
#define NULL

References NULL, and text.

Referenced by TimestampDir().

◆ ProcessExecute()

int ProcessExecute ( const wxString &  aCommandLine,
int  aFlags = wxEXEC_ASYNC,
wxProcess *  callback = nullptr 
)

Run a command in a child process.

Parameters
aCommandLineThe process and any arguments to it all in a single string.
aFlagsThe same args as allowed for wxExecute()
callbackwxProcess implementing OnTerminate to be run when the child process finishes
Returns
pid of process, 0 in case of error (like return values of wxExecute()).

Definition at line 46 of file common.cpp.

47 {
48  return (int) wxExecute( aCommandLine, aFlags, callback );
49 }

Referenced by doPrintFile(), ExecuteFile(), GetAssociatedDocument(), OpenFile(), OpenPDF(), and SCH_EDIT_FRAME::WriteNetListFile().

◆ ResolveUriByEnvVars()

const wxString ResolveUriByEnvVars ( const wxString &  aUri,
PROJECT aProject 
)

Replace any environment and/or text variables in file-path uris (leaving network-path URIs alone).

Definition at line 292 of file common.cpp.

293 {
294  wxString uri = ExpandTextVars( aUri, aProject );
295 
296  // URL-like URI: return as is.
297  wxURL url( uri );
298 
299  if( url.GetError() == wxURL_NOERR )
300  return uri;
301 
302  // Otherwise, the path points to a local file. Resolve environment variables if any.
303  return ExpandEnvVarSubstitutions( aUri, aProject );
304 }
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:64
const wxString ExpandEnvVarSubstitutions(const wxString &aString, PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:279

References ExpandEnvVarSubstitutions(), and ExpandTextVars().

Referenced by GetAssociatedDocument().

◆ TimestampDir()

long long TimestampDir ( const wxString &  aDirPath,
const wxString &  aFilespec 
)

A copy of ConvertFileTimeToWx() because wxWidgets left it as a static function private to src/common/filename.cpp.

TimestampDir

This routine offers SIGNIFICANT performance benefits over using wxWidgets to gather timestamps from matching files in a directory.

Parameters
aDirPaththe directory to search
aFilespeca (wildcarded) file spec to match against
Returns
a hash of the last-mod-dates of all matching files in the directory

Definition at line 516 of file common.cpp.

517 {
518  long long timestamp = 0;
519 
520 #if defined( __WIN32__ )
521  // Win32 version.
522  // Save time by not searching for each path twice: once in wxDir.GetNext() and once in
523  // wxFileName.GetModificationTime(). Also cuts out wxWidgets' string-matching and case
524  // conversion by staying on the MSW side of things.
525  std::wstring filespec( aDirPath.t_str() );
526  filespec += '\\';
527  filespec += aFilespec.t_str();
528 
529  WIN32_FIND_DATA findData;
530  wxDateTime lastModDate;
531 
532  HANDLE fileHandle = ::FindFirstFile( filespec.data(), &findData );
533 
534  if( fileHandle != INVALID_HANDLE_VALUE )
535  {
536  do
537  {
538  ConvertFileTimeToWx( &lastModDate, findData.ftLastWriteTime );
539  timestamp += lastModDate.GetValue().GetValue();
540  timestamp += findData.nFileSizeLow; // Get the file size (partial) as well to check for sneaky changes
541  }
542  while ( FindNextFile( fileHandle, &findData ) != 0 );
543  }
544 
545  FindClose( fileHandle );
546 #else
547  // POSIX version.
548  // Save time by not converting between encodings -- do everything on the file-system side.
549  std::string filespec( aFilespec.fn_str() );
550  std::string dir_path( aDirPath.fn_str() );
551 
552  DIR* dir = opendir( dir_path.c_str() );
553 
554  if( dir )
555  {
556  for( dirent* dir_entry = readdir( dir ); dir_entry; dir_entry = readdir( dir ) )
557  {
558  if( !matchWild( filespec.c_str(), dir_entry->d_name, true ) )
559  continue;
560 
561  std::string entry_path = dir_path + '/' + dir_entry->d_name;
562  struct stat entry_stat;
563 
564  if( wxCRT_Lstat( entry_path.c_str(), &entry_stat ) == 0 )
565  {
566  // Timestamp the source file, not the symlink
567  if( S_ISLNK( entry_stat.st_mode ) ) // wxFILE_EXISTS_SYMLINK
568  {
569  char buffer[ PATH_MAX + 1 ];
570  ssize_t pathLen = readlink( entry_path.c_str(), buffer, PATH_MAX );
571 
572  if( pathLen > 0 )
573  {
574  struct stat linked_stat;
575  buffer[ pathLen ] = '\0';
576  entry_path = dir_path + buffer;
577 
578  if( wxCRT_Lstat( entry_path.c_str(), &linked_stat ) == 0 )
579  {
580  entry_stat = linked_stat;
581  }
582  else
583  {
584  // if we couldn't lstat the linked file we'll have to just use
585  // the symbolic link info
586  }
587  }
588  }
589 
590  if( S_ISREG( entry_stat.st_mode ) ) // wxFileExists()
591  {
592  timestamp += entry_stat.st_mtime * 1000;
593  timestamp += entry_stat.st_size; // Get the file size as well to check for sneaky changes
594  }
595  }
596  else
597  {
598  // if we couldn't lstat the file itself all we can do is use the name
599  timestamp += (signed) std::hash<std::string>{}( std::string( dir_entry->d_name ) );
600  }
601  }
602 
603  closedir( dir );
604  }
605 #endif
606 
607  return timestamp;
608 }
bool matchWild(const char *pat, const char *text, bool dot_special)
Performance enhancements to file and directory operations.
Definition: common.cpp:374

References matchWild().

Referenced by FP_CACHE::GetTimestamp(), and GPCB_FPL_CACHE::GetTimestamp().

◆ WarnUserIfOperatingSystemUnsupported()

bool WarnUserIfOperatingSystemUnsupported ( )

Checks if the operating system is explicitly unsupported and displays a disclaimer message box.

Returns
true if the operating system is unsupported

Definition at line 610 of file common.cpp.

611 {
613  return false;
614 
615  wxMessageDialog dialog( NULL, _( "This operating system is not supported "
616  "by KiCad and its dependencies." ),
617  _( "Unsupported Operating System" ),
618  wxOK | wxICON_EXCLAMATION );
619 
620  dialog.SetExtendedMessage( _( "Any issues with KiCad on this system cannot "
621  "be reported to the official bugtracker." ) );
622  dialog.ShowModal();
623 
624  return true;
625 }
bool IsOperatingSystemUnsupported()
Checks if the Operating System is explicitly unsupported and we want to prevent users from sending bu...
Definition: gtk/app.cpp:51
#define NULL
#define _(s)

References _, KIPLATFORM::APP::IsOperatingSystemUnsupported(), and NULL.

Referenced by PGM_BASE::InitPgm(), and COMMON_CONTROL::ReportBug().