Fix a typo in xas.1 (thanks to Marcell)
[amule.git] / src / ExternalConnector.cpp
blob8d87e11b92580c74064079b7a5627cecb663ed8e
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "ExternalConnector.h"
27 #ifdef HAVE_CONFIG_H
28 #include "config.h" // Needed for VERSION and readline detection
29 #else
30 #include <common/ClientVersion.h>
31 #endif
33 #include <common/Format.h> // Needed for CFormat
34 #include <wx/tokenzr.h> // For wxStringTokenizer
36 // For readline
37 #ifdef HAVE_LIBREADLINE
38 #if defined(HAVE_READLINE_READLINE_H)
39 #include <readline/readline.h> // Do_not_auto_remove
40 #elif defined(HAVE_READLINE_H)
41 #include <readline.h> // Do_not_auto_remove
42 #else /* !defined(HAVE_READLINE_H) */
43 extern "C" char *readline (const char*);
44 #endif /* !defined(HAVE_READLINE_H) */
45 #else /* !defined(HAVE_READLINE_READLINE_H) */
46 /* no readline */
47 #endif /* HAVE_LIBREADLINE */
49 // For history
50 #ifdef HAVE_READLINE_HISTORY
51 #if defined(HAVE_READLINE_HISTORY_H)
52 #include <readline/history.h> // Do_not_auto_remove
53 #elif defined(HAVE_HISTORY_H)
54 #include <history.h> // Do_not_auto_remove
55 #else /* !defined(HAVE_HISTORY_H) */
56 extern "C" void add_history (const char*);
57 #endif /* defined(HAVE_READLINE_HISTORY_H) */
58 #else
59 /* no history */
60 #endif /* HAVE_READLINE_HISTORY */
63 #include <ec/cpp/ECFileConfig.h> // Needed for CECFileConfig
64 #include <common/MD5Sum.h>
65 #include "OtherFunctions.h" // Needed for GetPassword()
66 #include "MuleVersion.h" // Needed for GetMuleVersion()
68 #ifdef _MSC_VER // silly warnings about deprecated functions
69 #pragma warning(disable:4996)
70 #endif
72 //-------------------------------------------------------------------
74 CCommandTree::~CCommandTree()
76 DeleteContents(m_subcommands);
80 CCommandTree* CCommandTree::AddCommand(CCommandTree* command)
82 command->m_parent = this;
83 const wxString& cmd = command->m_command;
84 CmdPos_t it;
85 for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
86 if ((*it)->m_command > cmd) {
87 break;
90 m_subcommands.insert(it, command);
91 return command;
95 int CCommandTree::FindCommandId(const wxString& command, wxString& args, wxString& cmdstr) const
97 wxString cmd = command.BeforeFirst(wxT(' ')).Lower();
98 for (CmdPosConst_t it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
99 if ((*it)->m_command.Lower() == cmd) {
100 args = command.AfterFirst(wxT(' ')).Trim(false);
101 return (*it)->FindCommandId(args, args, cmdstr);
104 cmdstr = GetFullCommand().Lower();
105 if (m_params == CMD_PARAM_ALWAYS && args.IsEmpty()) {
106 return CMD_ERR_MUST_HAVE_PARAM;
107 } else if (m_params == CMD_PARAM_NEVER && !args.IsEmpty()) {
108 return CMD_ERR_NO_PARAM;
109 } else {
110 if ((m_cmd_id >= 0) && (m_cmd_id & CMD_DEPRECATED)) {
111 m_app.Show(wxT('\n') + m_verbose + wxT('\n'));
112 return m_cmd_id & ~CMD_DEPRECATED;
113 } else {
114 return m_cmd_id;
120 wxString CCommandTree::GetFullCommand() const
122 wxString cmd = m_command;
124 const CCommandTree *parent = m_parent;
125 while (parent && parent->m_parent) {
126 cmd = parent->m_command + wxT(" ") + cmd;
127 parent = parent->m_parent;
130 return cmd;
134 void CCommandTree::PrintHelpFor(const wxString& command) const
136 wxString cmd = command.BeforeFirst(wxT(' ')).Lower();
137 if (!cmd.IsEmpty()) {
138 for (CmdPosConst_t it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
139 if ((*it)->m_command.Lower() == cmd) {
140 (*it)->PrintHelpFor(command.AfterFirst(wxT(' ')).Trim(false));
141 return;
144 if (m_parent) {
145 m_app.Show(CFormat(_("Unknown extension '%s' for the '%s' command.\n")) % command % GetFullCommand());
146 } else {
147 m_app.Show(CFormat(_("Unknown command '%s'.\n")) % command);
149 } else {
150 wxString fullcmd = GetFullCommand();
151 if (!fullcmd.IsEmpty()) {
152 m_app.Show(fullcmd.Upper() + wxT(": ") + wxGetTranslation(m_short) + wxT("\n"));
153 if (!m_verbose.IsEmpty()) {
154 m_app.Show(wxT("\n"));
155 m_app.Show(wxGetTranslation(m_verbose));
158 if (m_params == CMD_PARAM_NEVER) {
159 m_app.Show(_("\nThis command cannot have an argument.\n"));
160 } else if (m_params == CMD_PARAM_ALWAYS) {
161 m_app.Show(_("\nThis command must have an argument.\n"));
163 if (m_cmd_id == CMD_ERR_INCOMPLETE) {
164 m_app.Show(_("\nThis command is incomplete, you must use one of the extensions below.\n"));
166 if (!m_subcommands.empty()) {
167 CmdPosConst_t it;
168 int maxlen = 0;
169 if (m_parent) {
170 m_app.Show(_("\nAvailable extensions:\n"));
171 } else {
172 m_app.Show(_("Available commands:\n"));
174 for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
175 if (!((*it)->m_cmd_id >= 0 && (*it)->m_cmd_id & CMD_DEPRECATED) || m_parent) {
176 int len = (*it)->m_command.Length();
177 if (len > maxlen) {
178 maxlen = len;
182 maxlen += 4;
183 for (it = m_subcommands.begin(); it != m_subcommands.end(); ++it) {
184 if (!((*it)->m_cmd_id >= 0 && (*it)->m_cmd_id & CMD_DEPRECATED) || m_parent) {
185 m_app.Show((*it)->m_command + wxString(wxT(' '), maxlen - (*it)->m_command.Length()) + wxGetTranslation((*it)->m_short) + wxT("\n"));
188 if (!m_parent) {
189 m_app.Show(CFormat(_("\nAll commands are case insensitive.\nType '%s <command>' to get detailed info on <command>.\n")) % wxT("help"));
193 m_app.Show(wxT("\n"));
196 //-------------------------------------------------------------------
198 CaMuleExternalConnector::CaMuleExternalConnector()
199 : m_configFile(NULL),
200 m_port(-1),
201 m_ZLIB(false),
202 m_KeepQuiet(false),
203 m_Verbose(false),
204 m_interactive(false),
205 m_commands(*this),
206 m_appname(NULL),
207 m_ECClient(NULL),
208 m_InputLine(NULL),
209 m_NeedsConfigSave(false),
210 m_locale(NULL),
211 m_strFullVersion(NULL),
212 m_strOSDescription(NULL)
214 SetAppName(wxT("aMule")); // Do not change!
217 CaMuleExternalConnector::~CaMuleExternalConnector()
219 delete m_configFile;
220 delete m_locale;
221 free(m_strFullVersion);
222 free(m_strOSDescription);
225 void CaMuleExternalConnector::OnInitCommandSet()
227 m_commands.AddCommand(wxT("Quit"), CMD_ID_QUIT, wxTRANSLATE("Exits from the application."), wxEmptyString);
228 m_commands.AddCommand(wxT("Exit"), CMD_ID_QUIT, wxTRANSLATE("Exits from the application."), wxEmptyString);
229 m_commands.AddCommand(wxT("Help"), CMD_ID_HELP, wxTRANSLATE("Show help."),
230 /* TRANSLATORS:
231 Do not translate the word 'help', it is a command to the program! */
232 wxTRANSLATE("To get help on a command, type 'help <command>'.\nTo get the full command list type 'help'.\n"));
235 void CaMuleExternalConnector::Show(const wxString &s)
237 if( !m_KeepQuiet ) {
238 printf("%s", (const char *)unicode2char(s));
239 #ifdef __WINDOWS__
240 fflush(stdout);
241 #endif
245 void CaMuleExternalConnector::ShowGreet()
247 wxString text = GetGreetingTitle();
248 int len = text.Length();
249 Show(wxT('\n') + wxString(wxT('-'), 22 + len) + wxT('\n'));
250 Show(wxT('|') + wxString(wxT(' '), 10) + text + wxString(wxT(' '), 10) + wxT('|') + wxT('\n'));
251 Show(wxString(wxT('-'), 22 + len) + wxT('\n'));
252 // Do not merge the line below, or translators could translate "Help"
253 Show(CFormat(_("\nUse '%s' for command list\n\n")) % wxT("Help"));
256 void CaMuleExternalConnector::Process_Answer(const wxString& answer)
258 wxStringTokenizer tokens(answer, wxT("\n"));
259 while ( tokens.HasMoreTokens() ) {
260 Show(wxT(" > ") + tokens.GetNextToken() + wxT("\n"));
264 bool CaMuleExternalConnector::Parse_Command(const wxString& buffer)
266 wxString cmd;
267 wxStringTokenizer tokens(buffer);
268 while (tokens.HasMoreTokens()) {
269 cmd += tokens.GetNextToken() + wxT(' ');
271 cmd.Trim(false);
272 cmd.Trim(true);
273 int cmd_ID = GetIDFromString(cmd);
274 if ( cmd_ID >= 0 ) {
275 cmd_ID = ProcessCommand(cmd_ID);
277 wxString error;
278 switch (cmd_ID) {
279 case CMD_ID_HELP:
280 m_commands.PrintHelpFor(GetCmdArgs());
281 break;
282 case CMD_ERR_SYNTAX:
283 error = _("Syntax error!");
284 break;
285 case CMD_ERR_PROCESS_CMD:
286 Show(_("Error processing command - should never happen! Report bug, please\n"));
287 break;
288 case CMD_ERR_NO_PARAM:
289 error = _("This command should not have any parameters.");
290 break;
291 case CMD_ERR_MUST_HAVE_PARAM:
292 error = _("This command must have a parameter.");
293 break;
294 case CMD_ERR_INVALID_ARG:
295 error = _("Invalid argument.");
296 break;
297 case CMD_ERR_INCOMPLETE:
298 error = _("This is an incomplete command.");
299 break;
301 if (!error.IsEmpty()) {
302 Show(error + wxT('\n'));
303 wxString helpStr(wxT("help"));
304 if (!GetLastCmdStr().IsEmpty()) {
305 helpStr << wxT(' ') << GetLastCmdStr();
307 Show(CFormat(_("Type '%s' to get more help.\n")) % helpStr);
309 return cmd_ID == CMD_ID_QUIT;
312 void CaMuleExternalConnector::GetCommand(const wxString &prompt, char* buffer, size_t buffer_size)
314 #ifdef HAVE_LIBREADLINE
315 char *text = readline(unicode2char(prompt + wxT("$ ")));
316 if (text && *text &&
317 (m_InputLine == 0 || strcmp(text,m_InputLine) != 0)) {
318 add_history (text);
320 if (m_InputLine)
321 free(m_InputLine);
322 m_InputLine = text;
323 #else
324 Show(prompt + wxT("$ "));
325 const char *text = fgets(buffer, buffer_size, stdin); // == buffer if ok, NULL if eof
326 #endif /* HAVE_LIBREADLINE */
327 if ( text ) {
328 size_t len = strlen(text);
329 if (len > buffer_size - 1) {
330 len = buffer_size - 1;
332 if (buffer != text) {
333 strncpy(buffer, text, len);
335 buffer[len] = 0;
336 } else {
337 strncpy(buffer, "quit", buffer_size);
341 void CaMuleExternalConnector::TextShell(const wxString &prompt)
343 char buffer[2048];
344 wxString buf;
346 bool The_End = false;
347 do {
348 GetCommand(prompt, buffer, sizeof buffer);
349 buf = char2unicode(buffer);
350 The_End = Parse_Command(buf);
351 } while ((!The_End) && (m_ECClient->IsSocketConnected()));
354 void CaMuleExternalConnector::ConnectAndRun(const wxString &ProgName, const wxString& ProgVersion)
356 if (m_NeedsConfigSave) {
357 SaveConfigFile();
358 return;
361 #ifdef SVNDATE
362 Show(CFormat(_("This is %s %s %s\n")) % wxString::FromAscii(m_appname) % wxT(VERSION) % wxT(SVNDATE));
363 #else
364 Show(CFormat(_("This is %s %s\n")) % wxString::FromAscii(m_appname) % wxT(VERSION));
365 #endif
367 // HostName, Port and Password
368 if ( m_password.IsEmpty() ) {
369 m_password = GetPassword(true);
370 // MD5 hash for an empty string, according to rfc1321.
371 if (m_password.Encode() == wxT("D41D8CD98F00B204E9800998ECF8427E")) {
372 m_password.Clear();
376 if (!m_password.IsEmpty()) {
378 // Create the socket
379 Show(_("\nCreating client...\n"));
380 m_ECClient = new CRemoteConnect(NULL);
381 m_ECClient->SetCapabilities(m_ZLIB, true, false); // ZLIB, UTF8 numbers, notification
383 // ConnectToCore is blocking since m_ECClient was initialized with NULL
384 if (!m_ECClient->ConnectToCore(m_host, m_port, wxT("foobar"), m_password.Encode(), ProgName, ProgVersion)) {
385 // no connection => close gracefully
386 if (!m_ECClient->GetServerReply().IsEmpty()) {
387 Show(CFormat(wxT("%s\n")) % m_ECClient->GetServerReply());
389 Show(CFormat(_("Connection Failed. Unable to connect to %s:%d\n")) % m_host % m_port);
390 } else {
391 // Authenticate ourselves
392 // ConnectToCore() already authenticated for us.
393 //m_ECClient->ConnectionEstablished();
394 Show(m_ECClient->GetServerReply()+wxT("\n"));
395 if (m_ECClient->IsSocketConnected()) {
396 if (m_interactive) {
397 ShowGreet();
399 Pre_Shell();
400 TextShell(ProgName);
401 Post_Shell();
402 if (m_interactive) {
403 Show(CFormat(_("\nOk, exiting %s...\n")) % ProgName);
407 m_ECClient->DestroySocket();
408 } else {
409 Show(_("Cannot connect with an empty password.\nYou must specify a password either in config file\nor on command-line, or enter one when asked.\n\nExiting...\n"));
413 void CaMuleExternalConnector::OnInitCmdLine(wxCmdLineParser& parser, const char* appname)
415 m_appname = appname;
417 parser.AddSwitch(wxEmptyString, wxT("help"),
418 _("Show this help text."),
419 wxCMD_LINE_PARAM_OPTIONAL);
420 parser.AddOption(wxT("h"), wxT("host"),
421 _("Host where aMule is running. (default: localhost)"),
422 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
423 parser.AddOption(wxT("p"), wxT("port"),
424 _("aMule's port for External Connection. (default: 4712)"),
425 wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL);
426 parser.AddOption(wxT("P"), wxT("password"),
427 _("External Connection password."),
428 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
429 parser.AddOption(wxT("f"), wxT("config-file"),
430 _("Read configuration from file."),
431 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
432 parser.AddSwitch(wxT("q"), wxT("quiet"),
433 _("Do not print any output to stdout."),
434 wxCMD_LINE_PARAM_OPTIONAL);
435 parser.AddSwitch(wxT("v"), wxT("verbose"),
436 _("Be verbose - show also debug messages."),
437 wxCMD_LINE_PARAM_OPTIONAL);
438 parser.AddOption(wxT("l"), wxT("locale"),
439 _("Sets program locale (language)."),
440 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
441 parser.AddSwitch(wxT("w"), wxT("write-config"),
442 _("Write command line options to config file."),
443 wxCMD_LINE_PARAM_OPTIONAL);
444 parser.AddOption(wxEmptyString, wxT("create-config-from"),
445 _("Creates config file based on aMule's config file."),
446 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
447 parser.AddSwitch(wxEmptyString, wxT("version"),
448 _("Print program version."),
449 wxCMD_LINE_PARAM_OPTIONAL);
452 bool CaMuleExternalConnector::OnCmdLineParsed(wxCmdLineParser& parser)
454 if (parser.Found(wxT("version"))) {
455 printf("%s %s\n", m_appname, (const char *)unicode2char(GetMuleVersion()));
456 return false;
459 if (!parser.Found(wxT("config-file"), &m_configFileName)) {
460 m_configFileName = wxT("remote.conf");
462 m_configDir = GetConfigDir(m_configFileName);
463 m_configFileName = m_configDir + m_configFileName;
465 wxString aMuleConfigFile;
466 if (parser.Found(wxT("create-config-from"), &aMuleConfigFile)) {
467 aMuleConfigFile = FinalizeFilename(aMuleConfigFile);
468 if (!::wxFileExists(aMuleConfigFile)) {
469 fprintf(stderr, "%s\n", (const char *)unicode2char(wxT("FATAL ERROR: File does not exist: ") + aMuleConfigFile));
470 exit(1);
472 CECFileConfig aMuleConfig(aMuleConfigFile);
473 LoadAmuleConfig(aMuleConfig);
474 SaveConfigFile();
475 m_configFile->Flush();
476 exit(0);
479 LoadConfigFile();
481 if ( !parser.Found(wxT("host"), &m_host) ) {
482 if ( m_host.IsEmpty() ) {
483 m_host = wxT("localhost");
487 long port;
488 if (parser.Found(wxT("port"), &port)) {
489 m_port = port;
492 wxString pass_plain;
493 if (parser.Found(wxT("password"), &pass_plain)) {
494 if (!pass_plain.IsEmpty()) {
495 m_password.Decode(MD5Sum(pass_plain).GetHash());
496 } else {
497 m_password.Clear();
501 if (parser.Found(wxT("write-config"))) {
502 m_NeedsConfigSave = true;
505 parser.Found(wxT("locale"), &m_language);
507 if (parser.Found(wxT("help"))) {
508 parser.Usage();
509 return false;
512 m_KeepQuiet = parser.Found(wxT("quiet"));
513 m_Verbose = parser.Found(wxT("verbose"));
515 return true;
518 void CaMuleExternalConnector::LoadAmuleConfig(CECFileConfig& cfg)
520 m_host = wxT("localhost");
521 m_port = cfg.Read(wxT("/ExternalConnect/ECPort"), 4712l);
522 cfg.ReadHash(wxT("/ExternalConnect/ECPassword"), &m_password);
523 m_language = cfg.Read(wxT("/eMule/Language"), wxEmptyString);
527 void CaMuleExternalConnector::LoadConfigFile()
529 if (!m_configFile) {
530 m_configFile = new CECFileConfig(m_configFileName);
532 if (m_configFile) {
533 m_language = m_configFile->Read(wxT("/Locale"), wxEmptyString);
534 m_host = m_configFile->Read(wxT("/EC/Host"), wxEmptyString);
535 m_port = m_configFile->Read(wxT("/EC/Port"), 4712l);
536 m_configFile->ReadHash(wxT("/EC/Password"), &m_password);
537 m_ZLIB = m_configFile->Read(wxT("/EC/ZLIB"), 1l) != 0;
541 void CaMuleExternalConnector::SaveConfigFile()
543 if (!wxFileName::DirExists(m_configDir)) {
544 wxFileName::Mkdir(m_configDir);
546 if (!m_configFile) {
547 m_configFile = new CECFileConfig(m_configFileName);
549 if (m_configFile) {
550 m_configFile->Write(wxT("/Locale"), m_language);
551 m_configFile->Write(wxT("/EC/Host"), m_host);
552 m_configFile->Write(wxT("/EC/Port"), m_port);
553 m_configFile->WriteHash(wxT("/EC/Password"), m_password);
557 bool CaMuleExternalConnector::OnInit()
559 #ifndef __WINDOWS__
560 #if wxUSE_ON_FATAL_EXCEPTION
561 // catch fatal exceptions
562 wxHandleFatalExceptions(true);
563 #endif
564 #endif
566 // If we didn't know that OnInit is called only once when creating the
567 // object, it could cause a memory leak. The two pointers below should
568 // be free()'d before assigning the new value.
569 // cppcheck-suppress publicAllocationError
570 m_strFullVersion = strdup((const char *)unicode2char(GetMuleVersion()));
571 m_strOSDescription = strdup((const char *)unicode2char(wxGetOsDescription()));
573 // Handle uncaught exceptions
574 InstallMuleExceptionHandler();
576 bool retval = wxApp::OnInit();
577 OnInitCommandSet();
578 InitCustomLanguages();
579 SetLocale(m_language);
580 return retval;
583 wxString CaMuleExternalConnector::SetLocale(const wxString& language)
585 if (!language.IsEmpty()) {
586 m_language = language;
587 if (m_locale) {
588 delete m_locale;
590 m_locale = new wxLocale;
591 InitLocale(*m_locale, StrLang2wx(language));
594 return m_locale == NULL ? wxString() : m_locale->GetCanonicalName();
597 #if !wxUSE_GUI && defined(__WXMAC__) && !wxCHECK_VERSION(2, 9, 0)
599 #include <wx/apptrait.h> // Do_not_auto_remove
600 #include <wx/stdpaths.h> // Do_not_auto_remove
602 class CaMuleExternalConnectorTraits : public wxConsoleAppTraits
604 public:
605 virtual wxStandardPathsBase& GetStandardPaths()
607 return s_stdPaths;
610 private:
611 static wxStandardPathsCF s_stdPaths;
614 wxStandardPathsCF CaMuleExternalConnectorTraits::s_stdPaths;
616 wxAppTraits* CaMuleExternalConnector::CreateTraits()
618 return new CaMuleExternalConnectorTraits;
621 #endif
623 #if wxUSE_ON_FATAL_EXCEPTION
624 // Gracefully handle fatal exceptions and print backtrace if possible
625 void CaMuleExternalConnector::OnFatalException()
627 /* Print the backtrace */
628 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
629 fprintf(stderr, "A fatal error has occurred and %s has crashed.\n", m_appname);
630 fprintf(stderr, "Please assist us in fixing this problem by posting the backtrace below in our\n");
631 fprintf(stderr, "'aMule Crashes' forum and include as much information as possible regarding the\n");
632 fprintf(stderr, "circumstances of this crash. The forum is located here:\n");
633 fprintf(stderr, " http://forum.amule.org/index.php?board=67.0\n");
634 fprintf(stderr, "If possible, please try to generate a real backtrace of this crash:\n");
635 fprintf(stderr, " http://wiki.amule.org/wiki/Backtraces\n\n");
636 fprintf(stderr, "----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n");
637 fprintf(stderr, "Current version is: %s %s\n", m_appname, m_strFullVersion);
638 fprintf(stderr, "Running on: %s\n\n", m_strOSDescription);
640 print_backtrace(1); // 1 == skip this function.
642 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
644 #endif
646 #ifdef __WXDEBUG__
647 void CaMuleExternalConnector::OnAssertFailure(const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg)
649 #if !defined wxUSE_STACKWALKER || !wxUSE_STACKWALKER
650 wxString errmsg = CFormat( wxT("%s:%s:%d: Assertion '%s' failed. %s") ) % file % func % line % cond % ( msg ? msg : wxT("") );
652 fprintf(stderr, "Assertion failed: %s\n", (const char*)unicode2char(errmsg));
654 // Skip the function-calls directly related to the assert call.
655 fprintf(stderr, "\nBacktrace follows:\n");
656 print_backtrace(3);
657 fprintf(stderr, "\n");
658 #else
659 wxApp::OnAssertFailure(file, line, func, cond, msg);
660 #endif
662 #endif
663 // File_checked_for_headers