Updated Spanish translation by tHatdUde
[amule.git] / src / ThreadTasks.cpp
bloba8ba3d0862eae1a92b11d7835cbdb429767e83ee
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2006-2011 Mikkel Schubert ( xaignar@amule.org / http:://www.amule.org )
5 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 //
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include <wx/app.h> // Needed for wxTheApp
30 #include "ThreadTasks.h" // Interface declarations
31 #include "PartFile.h" // Needed for CPartFile
32 #include "Logger.h" // Needed for Add(Debug)LogLine{C,N}
33 #include <common/Format.h> // Needed for CFormat
34 #include "amule.h" // Needed for theApp
35 #include "KnownFileList.h" // Needed for theApp->knownfiles
36 #include "Preferences.h" // Needed for thePrefs
37 #include "ScopedPtr.h" // Needed for CScopedPtr and CScopedArray
38 #include "PlatformSpecific.h" // Needed for CanFSHandleSpecialChars
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
44 //! This hash represents the value for an empty MD4 hashing
45 const byte g_emptyMD4Hash[16] = {
46 0x31, 0xD6, 0xCF, 0xE0, 0xD1, 0x6A, 0xE9, 0x31,
47 0xB7, 0x3C, 0x59, 0xD7, 0xE0, 0xC0, 0x89, 0xC0 };
50 ////////////////////////////////////////////////////////////
51 // CHashingTask
53 CHashingTask::CHashingTask(const CPath& path, const CPath& filename, const CPartFile* part)
54 // GetPrintable is used to improve the readability of the log.
55 : CThreadTask(wxT("Hashing"), path.JoinPaths(filename).GetPrintable(), (part ? ETP_High : ETP_Normal)),
56 m_path(path),
57 m_filename(filename),
58 m_toHash((EHashes)(EH_MD4 | EH_AICH)),
59 m_owner(part)
61 // We can only create the AICH hashset if the file is a knownfile or
62 // if the partfile is complete, since the MD4 hashset is checked first,
63 // so that the AICH hashset only gets assigned if the MD4 hashset
64 // matches what we expected. Due to the rareity of post-completion
65 // corruptions, this gives us a nice speedup in most cases.
66 if (part && !part->GetGapList().empty()) {
67 m_toHash = EH_MD4;
72 CHashingTask::CHashingTask(const CKnownFile* toAICHHash)
73 // GetPrintable is used to improve the readability of the log.
74 : CThreadTask(wxT("AICH Hashing"), toAICHHash->GetFilePath().JoinPaths(toAICHHash->GetFileName()).GetPrintable(), ETP_Low),
75 m_path(toAICHHash->GetFilePath()),
76 m_filename(toAICHHash->GetFileName()),
77 m_toHash(EH_AICH),
78 m_owner(toAICHHash)
83 void CHashingTask::Entry()
85 CFileAutoClose file;
87 CPath fullPath = m_path.JoinPaths(m_filename);
88 if (!file.Open(fullPath, CFile::read)) {
89 AddDebugLogLineC(logHasher,
90 CFormat(wxT("Warning, failed to open file, skipping: %s")) % fullPath);
91 return;
94 uint64 fileLength = 0;
95 try {
96 fileLength = file.GetLength();
97 } catch (const CIOFailureException&) {
98 AddDebugLogLineC(logHasher,
99 CFormat(wxT("Warning, failed to retrieve file-length, skipping: %s")) % fullPath);
100 return;
103 if (fileLength > MAX_FILE_SIZE) {
104 AddDebugLogLineC(logHasher,
105 CFormat(wxT("Warning, file is larger than supported size, skipping: %s")) % fullPath);
106 return;
107 } else if (fileLength == 0) {
108 if (m_owner) {
109 // It makes no sense to try to hash empty partfiles ...
110 wxFAIL;
111 } else {
112 // Zero-size partfiles should be hashed, but not zero-sized shared-files.
113 AddDebugLogLineC(logHasher,
114 CFormat(wxT("Warning, 0-size file, skipping: %s")) % fullPath);
117 return;
120 // For thread-safety, results are passed via a temporary file object.
121 CScopedPtr<CKnownFile> knownfile;
122 knownfile->m_filePath = m_path;
123 knownfile->SetFileName(m_filename);
124 knownfile->SetFileSize(fileLength);
125 knownfile->m_lastDateChanged = CPath::GetModificationTime(fullPath);
126 knownfile->m_AvailPartFrequency.insert(
127 knownfile->m_AvailPartFrequency.begin(),
128 knownfile->GetPartCount(), 0);
130 if ((m_toHash & EH_MD4) && (m_toHash & EH_AICH)) {
131 knownfile->GetAICHHashset()->FreeHashSet();
132 AddDebugLogLineN( logHasher, CFormat(
133 wxT("Starting to create MD4 and AICH hash for file: %s")) %
134 m_filename );
135 } else if ((m_toHash & EH_MD4)) {
136 AddDebugLogLineN( logHasher, CFormat(
137 wxT("Starting to create MD4 hash for file: %s")) % m_filename );
138 } else if ((m_toHash & EH_AICH)) {
139 knownfile->GetAICHHashset()->FreeHashSet();
140 AddDebugLogLineN( logHasher, CFormat(
141 wxT("Starting to create AICH hash for file: %s")) % m_filename );
142 } else {
143 wxCHECK_RET(0, (CFormat(wxT("No hashes requested for file, skipping: %s"))
144 % m_filename).GetString());
147 // This loops creates the part-hashes, loop-de-loop.
148 try {
149 for (uint16 part = 0; part < knownfile->GetPartCount() && !TestDestroy(); part++) {
150 SetHashingProgress(part + 1);
151 if (CreateNextPartHash(file, part, knownfile.get(), m_toHash) == false) {
152 AddDebugLogLineC(logHasher,
153 CFormat(wxT("Error while hashing file, skipping: %s"))
154 % m_filename);
156 SetHashingProgress(0);
157 return;
160 } catch (const CSafeIOException& e) {
161 AddDebugLogLineC(logHasher, wxT("IO exception while hashing file: ") + e.what());
162 SetHashingProgress(0);
163 return;
165 SetHashingProgress(0);
167 if ((m_toHash & EH_MD4) && !TestDestroy()) {
168 // If the file is < PARTSIZE, then the filehash is that one hash,
169 // otherwise, the filehash is the hash of the parthashes
170 if ( knownfile->m_hashlist.size() == 1 ) {
171 knownfile->m_abyFileHash = knownfile->m_hashlist[0];
172 knownfile->m_hashlist.clear();
173 } else if ( knownfile->m_hashlist.size() ) {
174 CMD4Hash hash;
175 knownfile->CreateHashFromHashlist(knownfile->m_hashlist, &hash);
176 knownfile->m_abyFileHash = hash;
177 } else {
178 // This should not happen!
179 wxFAIL;
183 // Did we create a AICH hashset?
184 if ((m_toHash & EH_AICH) && !TestDestroy()) {
185 CAICHHashSet* AICHHashSet = knownfile->GetAICHHashset();
187 AICHHashSet->ReCalculateHash(false);
188 if (AICHHashSet->VerifyHashTree(true) ) {
189 AICHHashSet->SetStatus(AICH_HASHSETCOMPLETE);
190 if (!AICHHashSet->SaveHashSet()) {
191 AddDebugLogLineC( logHasher,
192 CFormat(wxT("Warning, failed to save AICH hashset for file: %s"))
193 % m_filename );
198 if ((m_toHash == EH_AICH) && !TestDestroy()) {
199 CHashingEvent evt(MULE_EVT_AICH_HASHING, knownfile.release(), m_owner);
201 wxPostEvent(wxTheApp, evt);
202 } else if (!TestDestroy()) {
203 CHashingEvent evt(MULE_EVT_HASHING, knownfile.release(), m_owner);
205 wxPostEvent(wxTheApp, evt);
210 void CHashingTask::SetHashingProgress(uint16 part)
212 if (m_owner) {
213 m_owner->SetHashingProgress(part);
218 bool CHashingTask::CreateNextPartHash(CFileAutoClose& file, uint16 part, CKnownFile* owner, EHashes toHash)
220 wxCHECK_MSG(!file.Eof(), false, wxT("Unexpected EOF in CreateNextPartHash"));
222 const uint64 offset = part * PARTSIZE;
223 // We'll read at most PARTSIZE bytes per cycle
224 const uint64 partLength = owner->GetPartSize(part);
226 CMD4Hash hash;
227 CMD4Hash* md4Hash = ((toHash & EH_MD4) ? &hash : NULL);
228 CAICHHashTree* aichHash = NULL;
230 // Setup for AICH hashing
231 if (toHash & EH_AICH) {
232 aichHash = owner->GetAICHHashset()->m_pHashTree.FindHash(offset, partLength);
235 owner->CreateHashFromFile(file, offset, partLength, md4Hash, aichHash);
237 if (toHash & EH_MD4) {
238 // Store the md4 hash
239 owner->m_hashlist.push_back(hash);
241 // This is because of the ed2k implementation for parts. A 2 * PARTSIZE
242 // file i.e. will have 3 parts (see CKnownFile::SetFileSize for comments).
243 // So we have to create the hash for the 0-size data, which will be the default
244 // md4 hash for null data: 31D6CFE0D16AE931B73C59D7E0C089C0
245 if ((partLength == PARTSIZE) && file.Eof()) {
246 owner->m_hashlist.push_back(CMD4Hash(g_emptyMD4Hash));
250 return true;
254 void CHashingTask::OnLastTask()
256 if (GetType() == wxT("Hashing")) {
257 // To prevent rehashing in case of crashes, we
258 // explicity save the list of hashed files here.
259 theApp->knownfiles->Save();
261 // Make sure the AICH-hashes are up to date.
262 CThreadScheduler::AddTask(new CAICHSyncTask());
267 ////////////////////////////////////////////////////////////
268 // CAICHSyncTask
270 CAICHSyncTask::CAICHSyncTask()
271 : CThreadTask(wxT("AICH Syncronizing"), wxEmptyString, ETP_Low)
276 void CAICHSyncTask::Entry()
278 ConvertToKnown2ToKnown264();
280 AddDebugLogLineN( logAICHThread, wxT("Syncronization thread started.") );
282 // We collect all masterhashs which we find in the known2.met and store them in a list
283 std::list<CAICHHash> hashlist;
284 const CPath fullpath = CPath(theApp->ConfigDir + KNOWN2_MET_FILENAME);
286 CFile file;
287 if (!fullpath.FileExists()) {
288 // File does not exist. Try to create it to see if it can be created at all (and don't start hashing otherwise).
289 if (!file.Open(fullpath, CFile::write)) {
290 AddDebugLogLineC( logAICHThread, wxT("Error, failed to open 'known2_64.met' file!") );
291 return;
293 try {
294 file.WriteUInt8(KNOWN2_MET_VERSION);
295 } catch (const CIOFailureException& e) {
296 AddDebugLogLineC(logAICHThread, wxT("IO failure while creating hashlist (Aborting): ") + e.what());
297 return;
299 } else {
300 if (!file.Open(fullpath, CFile::read)) {
301 AddDebugLogLineC( logAICHThread, wxT("Error, failed to open 'known2_64.met' file!") );
302 return;
305 uint32 nLastVerifiedPos = 0;
306 try {
307 if (file.ReadUInt8() != KNOWN2_MET_VERSION) {
308 throw CEOFException(wxT("Invalid met-file header found, removing file."));
311 uint64 nExistingSize = file.GetLength();
312 while (file.GetPosition() < nExistingSize) {
313 // Read the next hash
314 hashlist.push_back(CAICHHash(&file));
316 uint32 nHashCount = file.ReadUInt32();
317 if (file.GetPosition() + nHashCount * CAICHHash::GetHashSize() > nExistingSize){
318 throw CEOFException(wxT("Hashlist ends past end of file."));
321 // skip the rest of this hashset
322 nLastVerifiedPos = file.Seek(nHashCount * HASHSIZE, wxFromCurrent);
324 } catch (const CEOFException&) {
325 AddDebugLogLineC(logAICHThread, wxT("Hashlist corrupted, truncating file."));
326 file.Close();
327 file.Reopen(CFile::read_write);
328 file.SetLength(nLastVerifiedPos);
329 } catch (const CIOFailureException& e) {
330 AddDebugLogLineC(logAICHThread, wxT("IO failure while reading hashlist (Aborting): ") + e.what());
332 return;
335 AddDebugLogLineN( logAICHThread, wxT("Masterhashes of known files have been loaded.") );
338 // Now we check that all files which are in the sharedfilelist have a
339 // corresponding hash in our list. Those how don't are queued for hashing.
340 theApp->sharedfiles->CheckAICHHashes(hashlist);
344 bool CAICHSyncTask::ConvertToKnown2ToKnown264()
346 // converting known2.met to known2_64.met to support large files
347 // changing hashcount from uint16 to uint32
349 const CPath oldfullpath = CPath(theApp->ConfigDir + OLD_KNOWN2_MET_FILENAME);
350 const CPath newfullpath = CPath(theApp->ConfigDir + KNOWN2_MET_FILENAME);
352 if (newfullpath.FileExists() || !oldfullpath.FileExists()) {
353 // In this case, there is nothing that we need to do.
354 return false;
357 CFile oldfile;
358 CFile newfile;
360 if (!oldfile.Open(oldfullpath, CFile::read)) {
361 AddDebugLogLineC(logAICHThread, wxT("Failed to open 'known2.met' file."));
363 // else -> known2.met also doesn't exists, so nothing to convert
364 return false;
368 if (!newfile.Open(newfullpath, CFile::write_excl)) {
369 AddDebugLogLineC(logAICHThread, wxT("Failed to create 'known2_64.met' file."));
371 return false;
374 AddLogLineN(CFormat(_("Converting old AICH hashsets in '%s' to 64b in '%s'."))
375 % OLD_KNOWN2_MET_FILENAME % KNOWN2_MET_FILENAME);
377 try {
378 newfile.WriteUInt8(KNOWN2_MET_VERSION);
380 while (newfile.GetPosition() < oldfile.GetLength()) {
381 CAICHHash aichHash(&oldfile);
382 uint32 nHashCount = oldfile.ReadUInt16();
384 CScopedArray<byte> buffer(nHashCount * CAICHHash::GetHashSize());
386 oldfile.Read(buffer.get(), nHashCount * CAICHHash::GetHashSize());
387 newfile.Write(aichHash.GetRawHash(), CAICHHash::GetHashSize());
388 newfile.WriteUInt32(nHashCount);
389 newfile.Write(buffer.get(), nHashCount * CAICHHash::GetHashSize());
391 newfile.Flush();
392 } catch (const CEOFException& e) {
393 AddDebugLogLineC(logAICHThread, wxT("Error reading old 'known2.met' file.") + e.what());
394 return false;
395 } catch (const CIOFailureException& e) {
396 AddDebugLogLineC(logAICHThread, wxT("IO error while converting 'known2.met' file: ") + e.what());
397 return false;
400 // FIXME LARGE FILES (uncomment)
401 //DeleteFile(oldfullpath);
403 return true;
408 ////////////////////////////////////////////////////////////
409 // CCompletionTask
412 CCompletionTask::CCompletionTask(const CPartFile* file)
413 // GetPrintable is used to improve the readability of the log.
414 : CThreadTask(wxT("Completing"), file->GetFullName().GetPrintable(), ETP_High),
415 m_filename(file->GetFileName()),
416 m_metPath(file->GetFullName()),
417 m_category(file->GetCategory()),
418 m_owner(file),
419 m_error(false)
421 wxASSERT(m_filename.IsOk());
422 wxASSERT(m_metPath.IsOk());
423 wxASSERT(m_owner);
427 void CCompletionTask::Entry()
429 CPath targetPath;
432 #ifndef AMULE_DAEMON
433 // Prevent the preference values from changing underneeth us.
434 wxMutexGuiLocker guiLock;
435 #else
436 //#warning Thread-safety needed
437 #endif
439 targetPath = theApp->glob_prefs->GetCategory(m_category)->path;
440 if (!targetPath.DirExists()) {
441 targetPath = thePrefs::GetIncomingDir();
445 CPath dstName = m_filename.Cleanup(true, !PlatformSpecific::CanFSHandleSpecialChars(targetPath));
447 // Avoid empty filenames ...
448 if (!dstName.IsOk()) {
449 dstName = CPath(wxT("Unknown"));
452 if (m_filename != dstName) {
453 AddLogLineC(CFormat(_("WARNING: The filename '%s' is invalid and has been renamed to '%s'.")) % m_filename % dstName);
456 // Avoid saving to an already existing filename
457 CPath newName = targetPath.JoinPaths(dstName);
458 for (unsigned count = 0; newName.FileExists(); ++count) {
459 wxString postfix = CFormat(wxT("(%u)")) % count;
461 newName = targetPath.JoinPaths(dstName.AddPostfix(postfix));
464 if (newName != targetPath.JoinPaths(dstName)) {
465 AddLogLineC(CFormat(_("WARNING: The file '%s' already exists, new file renamed to '%s'.")) % dstName % newName.GetFullName());
468 // Move will handle dirs on the same partition, otherwise copy is needed.
469 CPath partfilename = m_metPath.RemoveExt();
470 if (!CPath::RenameFile(partfilename, newName)) {
471 if (!CPath::CloneFile(partfilename, newName, true)) {
472 m_error = true;
473 return;
476 if (!CPath::RemoveFile(partfilename)) {
477 AddDebugLogLineC(logPartFile, CFormat(wxT("WARNING: Could not remove original '%s' after creating backup")) % partfilename);
481 // Removes the various other data-files
482 const wxChar* otherMetExt[] = { wxT(""), PARTMET_BAK_EXT, wxT(".seeds"), NULL };
483 for (size_t i = 0; otherMetExt[i]; ++i) {
484 CPath toRemove = m_metPath.AppendExt(otherMetExt[i]);
486 if (toRemove.FileExists()) {
487 if (!CPath::RemoveFile(toRemove)) {
488 AddDebugLogLineC(logPartFile, CFormat(wxT("WARNING: Failed to delete %s")) % toRemove);
493 m_newName = newName;
497 void CCompletionTask::OnExit()
499 // Notify the app that the completion has finished for this file.
500 CCompletionEvent evt(m_error, m_owner, m_newName);
502 wxPostEvent(wxTheApp, evt);
507 ////////////////////////////////////////////////////////////
508 // CAllocateFileTask
510 #ifdef HAVE_FALLOCATE
511 # ifndef _GNU_SOURCE
512 # define _GNU_SOURCE
513 # endif
514 # ifdef HAVE_FCNTL_H
515 # include <fcntl.h>
516 # endif
517 # include <linux/falloc.h>
518 #elif defined HAVE_SYS_FALLOCATE
519 # include <sys/syscall.h>
520 # include <sys/types.h>
521 # include <unistd.h>
522 #elif defined HAVE_POSIX_FALLOCATE
523 # define _XOPEN_SOURCE 600
524 # include <stdlib.h>
525 # ifdef HAVE_FCNTL_H
526 # include <fcntl.h>
527 # endif
528 #endif
529 #include <stdlib.h>
530 #include <errno.h>
532 CAllocateFileTask::CAllocateFileTask(CPartFile *file, bool pause)
533 // GetPrintable is used to improve the readability of the log.
534 : CThreadTask(wxT("Allocating"), file->GetFullName().RemoveExt().GetPrintable(), ETP_High),
535 m_file(file), m_pause(pause), m_result(ENOSYS)
537 wxASSERT(file != NULL);
540 void CAllocateFileTask::Entry()
542 if (m_file->GetFileSize() == 0) {
543 m_result = 0;
544 return;
547 uint64_t minFree = thePrefs::IsCheckDiskspaceEnabled() ? thePrefs::GetMinFreeDiskSpace() : 0;
548 int64_t freeSpace = CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
550 // Don't even try to allocate, if there's no space to complete the operation.
551 if (freeSpace != wxInvalidOffset) {
552 if ((uint64_t)freeSpace < m_file->GetFileSize() + minFree) {
553 m_result = ENOSPC;
554 return;
558 CFile file;
559 file.Open(m_file->GetFullName().RemoveExt(), CFile::read_write);
561 #ifdef __WXMSW__
562 try {
563 // File is already created as non-sparse, so we only need to set the length.
564 // This will fail to allocate the file e.g. under wine on linux/ext3,
565 // but works with NTFS and FAT32.
566 file.Seek(m_file->GetFileSize() - 1, wxFromStart);
567 file.WriteUInt8(0);
568 file.Close();
569 m_result = 0;
570 } catch (const CSafeIOException&) {
571 m_result = errno;
573 #else
574 // Use kernel level routines if possible
575 # ifdef HAVE_FALLOCATE
576 m_result = fallocate(file.fd(), 0, 0, m_file->GetFileSize());
577 # elif defined HAVE_SYS_FALLOCATE
578 m_result = syscall(SYS_fallocate, file.fd(), 0, (loff_t)0, (loff_t)m_file->GetFileSize());
579 if (m_result == -1) {
580 m_result = errno;
582 # elif defined HAVE_POSIX_FALLOCATE
583 // otherwise use glibc implementation, if available
584 m_result = posix_fallocate(file.fd(), 0, m_file->GetFileSize());
585 # endif
587 if (m_result != 0 && m_result != ENOSPC) {
588 // If everything else fails, use slow-and-dirty method of allocating the file: write the whole file with zeroes.
589 # define BLOCK_SIZE 1048576 /* Write 1 MB blocks */
590 void *zero = calloc(1, BLOCK_SIZE);
591 if (zero != NULL) {
592 try {
593 uint64_t size = m_file->GetFileSize();
594 for (; size >= BLOCK_SIZE; size -= BLOCK_SIZE) {
595 file.Write(zero, BLOCK_SIZE);
597 if (size > 0) {
598 file.Write(zero, size);
600 file.Close();
601 m_result = 0;
602 } catch (const CSafeIOException&) {
603 m_result = errno;
605 free(zero);
606 } else {
607 m_result = ENOMEM;
611 #endif
612 if (file.IsOpened()) {
613 file.Close();
617 void CAllocateFileTask::OnExit()
619 // Notify the app that the preallocation has finished for this file.
620 CAllocFinishedEvent evt(m_file, m_pause, m_result);
622 wxPostEvent(wxTheApp, evt);
627 ////////////////////////////////////////////////////////////
628 // CHashingEvent
630 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_HASHING)
631 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_AICH_HASHING)
633 CHashingEvent::CHashingEvent(wxEventType type, CKnownFile* result, const CKnownFile* owner)
634 : wxEvent(-1, type),
635 m_owner(owner),
636 m_result(result)
641 wxEvent* CHashingEvent::Clone() const
643 return new CHashingEvent(GetEventType(), m_result, m_owner);
647 const CKnownFile* CHashingEvent::GetOwner() const
649 return m_owner;
653 CKnownFile* CHashingEvent::GetResult() const
655 return m_result;
661 ////////////////////////////////////////////////////////////
662 // CCompletionEvent
664 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_FILE_COMPLETED)
667 CCompletionEvent::CCompletionEvent(bool errorOccured, const CPartFile* owner, const CPath& fullPath)
668 : wxEvent(-1, MULE_EVT_FILE_COMPLETED),
669 m_fullPath(fullPath),
670 m_owner(owner),
671 m_error(errorOccured)
676 wxEvent* CCompletionEvent::Clone() const
678 return new CCompletionEvent(m_error, m_owner, m_fullPath);
682 bool CCompletionEvent::ErrorOccured() const
684 return m_error;
688 const CPartFile* CCompletionEvent::GetOwner() const
690 return m_owner;
694 const CPath& CCompletionEvent::GetFullPath() const
696 return m_fullPath;
700 ////////////////////////////////////////////////////////////
701 // CAllocFinishedEvent
703 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_ALLOC_FINISHED)
705 wxEvent *CAllocFinishedEvent::Clone() const
707 return new CAllocFinishedEvent(m_file, m_pause, m_result);
710 // File_checked_for_headers