From 1fffad3f437a224769ce48659db1eaadfb5deeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 3 Aug 2008 10:45:51 +0000 Subject: [PATCH] Refactored a new class FileIterator from Grepper that will make adding node monitoring easier. Also, FileIterator will be split to make the code cleaner with regards to folder or selection mode. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26752 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/text_search/FileIterator.cpp | 225 ++++++++++++++++++++++++++ src/apps/text_search/FileIterator.h | 76 +++++++++ src/apps/text_search/GlobalDefs.h | 8 + src/apps/text_search/GrepWindow.cpp | 23 ++- src/apps/text_search/Grepper.cpp | 188 ++------------------- src/apps/text_search/Grepper.h | 40 ++--- src/apps/text_search/Jamfile | 1 + src/apps/text_search/Model.h | 8 +- 8 files changed, 348 insertions(+), 221 deletions(-) create mode 100644 src/apps/text_search/FileIterator.cpp create mode 100644 src/apps/text_search/FileIterator.h diff --git a/src/apps/text_search/FileIterator.cpp b/src/apps/text_search/FileIterator.cpp new file mode 100644 index 0000000000..9b1ac9bfe8 --- /dev/null +++ b/src/apps/text_search/FileIterator.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2008 Stephan Aßmus + * Copyright (c) 1998-2007 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "FileIterator.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "Model.h" + +using std::nothrow; + +// TODO: stippi: Check if this is a the best place to maintain a global +// list of files and folders for node monitoring. It should probably monitor +// every file that was grepped, as well as every visited (sub) folder. +// For the moment I don't know the life cycle of the FileIterator object. + + +FileIterator::FileIterator(Model* model) + : fDirectories(10), + fCurrentDir(new (nothrow) BDirectory(&model->fDirectory)), + fCurrentRef(0), + fModel(model) +{ + if (!fCurrentDir || !fDirectories.AddItem(fCurrentDir)) { + // init error + delete fCurrentDir; + fCurrentDir = NULL; + } +} + + +FileIterator::~FileIterator() +{ + for (int32 i = fDirectories.CountItems() - 1; i >= 0; i--) + delete (BDirectory*)fDirectories.ItemAt(i); +} + + +bool +FileIterator::IsValid() const +{ + return fCurrentDir != NULL; +} + + +bool +FileIterator::GetNextName(char* buffer) +{ + BEntry entry; + struct stat fileStat; + + while (true) { + // Traverse the directory to get a new BEntry. + // _GetNextEntry returns false if there are no + // more entries, and we exit the loop. + + if (!_GetNextEntry(entry)) + return false; + + // If the entry is a subdir, then add it to the + // list of directories and continue the loop. + // If the entry is a file and we can grep it + // (i.e. it is a text file), then we're done + // here. Otherwise, continue with the next entry. + + if (entry.GetStat(&fileStat) == B_OK) { + if (S_ISDIR(fileStat.st_mode)) { + // subdir + _ExamineSubdir(entry); + } else { + // file or a (non-traversed) symbolic link + if (_ExamineFile(entry, buffer)) + return true; + } + } + } +} + + +// #pragma mark - private + + +bool +FileIterator::_GetNextEntry(BEntry& entry) +{ + if (fDirectories.CountItems() == 1) + return _GetTopEntry(entry); + else + return _GetSubEntry(entry); +} + + +bool +FileIterator::_GetTopEntry(BEntry& entry) +{ + // If the user selected one or more files, we must look + // at the "refs" inside the message that was passed into + // our add-on's process_refs(). If the user didn't select + // any files, we will simply read all the entries from the + // current working directory. + + entry_ref fileRef; + + if (fModel->fSelectedFiles.FindRef("refs", fCurrentRef, &fileRef) == B_OK) { + entry.SetTo(&fileRef, fModel->fRecurseLinks); + ++fCurrentRef; + return true; + } else if (fCurrentRef > 0) { + // when we get here, we have processed + // all the refs from the message + return false; + } else if (fCurrentDir != NULL) { + // examine the whole directory + return fCurrentDir->GetNextEntry(&entry, + fModel->fRecurseLinks) == B_OK; + } + + return false; +} + + +bool +FileIterator::_GetSubEntry(BEntry& entry) +{ + if (!fCurrentDir) + return false; + + if (fCurrentDir->GetNextEntry(&entry, fModel->fRecurseLinks) == B_OK) + return true; + + // If we get here, there are no more entries in + // this subdir, so return to the parent directory. + + fDirectories.RemoveItem(fCurrentDir); + delete fCurrentDir; + fCurrentDir = (BDirectory*)fDirectories.LastItem(); + + return _GetNextEntry(entry); +} + + +void +FileIterator::_ExamineSubdir(BEntry& entry) +{ + if (!fModel->fRecurseDirs) + return; + + if (fModel->fSkipDotDirs) { + char nameBuf[B_FILE_NAME_LENGTH]; + if (entry.GetName(nameBuf) == B_OK) { + if (*nameBuf == '.') + return; + } + } + + BDirectory* dir = new (nothrow) BDirectory(&entry); + if (dir == NULL || dir->InitCheck() != B_OK + || !fDirectories.AddItem(dir)) { + // clean up + delete dir; + return; + } + + fCurrentDir = dir; +} + + +bool +FileIterator::_ExamineFile(BEntry& entry, char* buffer) +{ + BPath path; + if (entry.GetPath(&path) != B_OK) + return false; + + strcpy(buffer, path.Path()); + + if (!fModel->fTextOnly) + return true; + + BNode node(&entry); + BNodeInfo nodeInfo(&node); + char mimeTypeString[B_MIME_TYPE_LENGTH]; + + if (nodeInfo.GetType(mimeTypeString) == B_OK) { + BMimeType mimeType(mimeTypeString); + BMimeType superType; + + if (mimeType.GetSupertype(&superType) == B_OK) { + if (strcmp("text", superType.Type()) == 0 + || strcmp("message", superType.Type()) == 0) { + return true; + } + } + } + + return false; +} + diff --git a/src/apps/text_search/FileIterator.h b/src/apps/text_search/FileIterator.h new file mode 100644 index 0000000000..abeb44aade --- /dev/null +++ b/src/apps/text_search/FileIterator.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2008 Stephan Aßmus + * Copyright (c) 1998-2007 Matthijs Hollemans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef FILE_ITERATOR_H +#define FILE_ITERATOR_H + +#include + +class BEntry; +class BDirectory; +class Model; + +// TODO: split into Folder and MessageFileIterator (_GetTopEntry) + +// Provides an interface to retrieve the next file that should be grepped +// for the search string. +class FileIterator { +public: + FileIterator(Model* model); + virtual ~FileIterator(); + + virtual bool IsValid() const; + + // Returns the full path name of the next file. + virtual bool GetNextName(char* buffer); + +private: + // Looks for the next entry. + bool _GetNextEntry(BEntry& entry); + + // Looks for the next entry in the top-level dir. + bool _GetTopEntry(BEntry& entry); + + // Looks for the next entry in a subdir. + bool _GetSubEntry(BEntry& entry); + + // Determines whether we can add a subdir. + void _ExamineSubdir(BEntry& entry); + + // Determines whether we can grep a file. + bool _ExamineFile(BEntry& entry, char* buffer); + +private: + // Contains pointers to BDirectory objects. + BList fDirectories; + + // The directory we are currently looking at. + BDirectory* fCurrentDir; + + // The ref number we are currently looking at. + int32 fCurrentRef; + + // The directory or files to grep on. + Model* fModel; +}; + +#endif // FILE_ITERATOR_H diff --git a/src/apps/text_search/GlobalDefs.h b/src/apps/text_search/GlobalDefs.h index 8a63685ef7..494b553a63 100644 --- a/src/apps/text_search/GlobalDefs.h +++ b/src/apps/text_search/GlobalDefs.h @@ -5,7 +5,15 @@ #ifndef GLOBAL_DEFS_H #define GLOBAL_DEFS_H + #define APP_SIGNATURE "application/x-vnd.mahlzeit.trackergrep" #define APP_NAME "TextSearch" +#define PREFS_FILE "TextSearchSettings" +#define HISTORY_LIMIT 20 + +#define TRACKER_SIGNATURE "application/x-vnd.Be-TRAK" +#define PE_SIGNATURE "application/x-vnd.beunited.pe" + + #endif // GLOBAL_DEFS_H diff --git a/src/apps/text_search/GrepWindow.cpp b/src/apps/text_search/GrepWindow.cpp index cbc9f82a8e..4e6b575b57 100644 --- a/src/apps/text_search/GrepWindow.cpp +++ b/src/apps/text_search/GrepWindow.cpp @@ -38,6 +38,7 @@ #include #include +#include "FileIterator.h" #include "GlobalDefs.h" #include "Grepper.h" #include "Translation.h" @@ -112,10 +113,7 @@ GrepWindow::GrepWindow(BMessage* message) GrepWindow::~GrepWindow() { - if (fModel->fState == STATE_SEARCH) { - fGrepper->Cancel(); - } - + delete fGrepper; delete fModel; } @@ -708,8 +706,21 @@ GrepWindow::_OnStartCancel() fOldPattern = fSearchText->Text(); - fGrepper = new Grepper(fOldPattern.String(), fModel); - fGrepper->Start(); + FileIterator* iterator = new (nothrow) FileIterator(fModel); + fGrepper = new (nothrow) Grepper(fOldPattern.String(), fModel, + iterator); + if (fGrepper != NULL && fGrepper->IsValid()) + fGrepper->Start(); + else { + // roll back in case of problems + if (fGrepper == NULL) + delete iterator; + delete fGrepper; + fGrepper = NULL; + fModel->fState = STATE_CANCEL; + // TODO: better notification to user + fprintf(stderr, "Out of memory.\n"); + } } else if (fModel->fState == STATE_SEARCH) { fModel->fState = STATE_CANCEL; fGrepper->Cancel(); diff --git a/src/apps/text_search/Grepper.cpp b/src/apps/text_search/Grepper.cpp index b2e6cfb6a0..9b9ba805a9 100644 --- a/src/apps/text_search/Grepper.cpp +++ b/src/apps/text_search/Grepper.cpp @@ -33,6 +33,8 @@ #include #include +#include "FileIterator.h" + using std::nothrow; // TODO: stippi: Check if this is a the best place to maintain a global @@ -88,24 +90,13 @@ strdup_from_utf8(uint32 encode, const char* src, int32 length) } -Grepper::Grepper(const char* pattern, Model* model) - : fDirectories(new (nothrow) BList(10)), - fCurrentDir(new (nothrow) BDirectory(&model->fDirectory)), - fCurrentRef(0), - fPattern(NULL), +Grepper::Grepper(const char* pattern, Model* model, FileIterator* iterator) + : fPattern(NULL), fModel(model), + fIterator(iterator), fThreadId(-1), fMustQuit(false) { - if (!fCurrentDir || !fDirectories || !fDirectories->AddItem(fCurrentDir)) { - // init error - delete fCurrentDir; - fCurrentDir = NULL; - delete fDirectories; - fDirectories = NULL; - return; - } - if (fModel->fEncoding) { char *src = strdup_from_utf8(fModel->fEncoding, pattern, strlen(pattern)); @@ -119,26 +110,17 @@ Grepper::Grepper(const char* pattern, Model* model) Grepper::~Grepper() { Cancel(); - free(fPattern); - - // If the thread terminated normally, then there is only - // one object in the list: the initial directory. But if - // the user aborted the search, there may be more. - - if (fDirectories) { - for (int32 i = fDirectories->CountItems() - 1; i >= 0; i--) - delete static_cast(fDirectories->ItemAt(i)); - - delete fDirectories; - } + delete fIterator; } bool Grepper::IsValid() const { - return fPattern != NULL && fDirectories != NULL && fCurrentDir != NULL; + if (fIterator == NULL || !fIterator->IsValid()) + return false; + return fPattern != NULL && fModel != NULL; } @@ -192,7 +174,7 @@ Grepper::_GrepperThread() sprintf(fileName, "/boot/var/tmp/SearchText%ld", fThreadId); tempFile.SetTo(fileName); - while (!fMustQuit && _GetNextName(fileName)) { + while (!fMustQuit && fIterator->GetNextName(fileName)) { message.MakeEmpty(); message.what = MSG_REPORT_FILE_NAME; message.AddString("filename", fileName); @@ -344,153 +326,3 @@ Grepper::_EscapeSpecialChars(char* buffer, ssize_t bufferSize) return result; } - -bool -Grepper::_GetNextName(char* buffer) -{ - BEntry entry; - struct stat fileStat; - - while (true) { - // Traverse the directory to get a new BEntry. - // _GetNextEntry returns false if there are no - // more entries, and we exit the loop. - - if (!_GetNextEntry(entry)) - return false; - - // If the entry is a subdir, then add it to the - // list of directories and continue the loop. - // If the entry is a file and we can grep it - // (i.e. it is a text file), then we're done - // here. Otherwise, continue with the next entry. - - if (entry.GetStat(&fileStat) == B_OK) { - if (S_ISDIR(fileStat.st_mode)) { - // subdir - _ExamineSubdir(entry); - } else { - // file or a (non-traversed) symbolic link - if (_ExamineFile(entry, buffer)) - return true; - } - } - } -} - - -bool -Grepper::_GetNextEntry(BEntry& entry) -{ - if (fDirectories->CountItems() == 1) - return _GetTopEntry(entry); - else - return _GetSubEntry(entry); -} - - -bool -Grepper::_GetTopEntry(BEntry& entry) -{ - // If the user selected one or more files, we must look - // at the "refs" inside the message that was passed into - // our add-on's process_refs(). If the user didn't select - // any files, we will simply read all the entries from the - // current working directory. - - entry_ref fileRef; - - if (fModel->fSelectedFiles.FindRef("refs", - fCurrentRef, &fileRef) == B_OK) { - entry.SetTo(&fileRef, fModel->fRecurseLinks); - ++fCurrentRef; - return true; - } else if (fCurrentRef > 0) { - // when we get here, we have processed - // all the refs from the message - return false; - } else { - // examine the whole directory - return fCurrentDir->GetNextEntry(&entry, - fModel->fRecurseLinks) == B_OK; - } -} - - -bool -Grepper::_GetSubEntry(BEntry& entry) -{ - if (!fCurrentDir) - return false; - - if (fCurrentDir->GetNextEntry(&entry, fModel->fRecurseLinks) == B_OK) - return true; - - // If we get here, there are no more entries in - // this subdir, so return to the parent directory. - - fDirectories->RemoveItem(fCurrentDir); - delete fCurrentDir; - fCurrentDir = (BDirectory*)fDirectories->LastItem(); - - return _GetNextEntry(entry); -} - - -void -Grepper::_ExamineSubdir(BEntry& entry) -{ - if (!fModel->fRecurseDirs) - return; - - if (fModel->fSkipDotDirs) { - char nameBuf[B_FILE_NAME_LENGTH]; - if (entry.GetName(nameBuf) == B_OK) { - if (*nameBuf == '.') - return; - } - } - - BDirectory* dir = new (nothrow) BDirectory(&entry); - if (dir == NULL || dir->InitCheck() != B_OK - || !fDirectories->AddItem(dir)) { - // clean up - delete dir; - return; - } - - fCurrentDir = dir; -} - - -bool -Grepper::_ExamineFile(BEntry& entry, char* buffer) -{ - BPath path; - if (entry.GetPath(&path) != B_OK) - return false; - - strcpy(buffer, path.Path()); - - if (!fModel->fTextOnly) - return true; - - BNode node(&entry); - BNodeInfo nodeInfo(&node); - char mimeTypeString[B_MIME_TYPE_LENGTH]; - - if (nodeInfo.GetType(mimeTypeString) == B_OK) { - BMimeType mimeType(mimeTypeString); - BMimeType superType; - - if (mimeType.GetSupertype(&superType) == B_OK) { - if (strcmp("text", superType.Type()) == 0 - || strcmp("message", superType.Type()) == 0) { - return true; - } - } - } - - return false; -} - diff --git a/src/apps/text_search/Grepper.h b/src/apps/text_search/Grepper.h index 9043115be6..a8883b7825 100644 --- a/src/apps/text_search/Grepper.h +++ b/src/apps/text_search/Grepper.h @@ -24,10 +24,13 @@ #include "Model.h" +class FileIterator; + // Executes "grep" in a background thread. class Grepper { public: - Grepper(const char* pattern, Model* model); + Grepper(const char* pattern, Model* model, + FileIterator* iterator); virtual ~Grepper(); bool IsValid() const; @@ -49,44 +52,19 @@ private: // to prevent the shell from misinterpreting them. bool _EscapeSpecialChars(char* buffer, ssize_t bufferSize); - - // Returns the full path name of the next file. - bool _GetNextName(char* buffer); - - // Looks for the next entry. - bool _GetNextEntry(BEntry& entry); - - // Looks for the next entry in the top-level dir. - bool _GetTopEntry(BEntry& entry); - - // Looks for the next entry in a subdir. - bool _GetSubEntry(BEntry& entry); - - // Determines whether we can add a subdir. - void _ExamineSubdir(BEntry& entry); - - // Determines whether we can grep a file. - bool _ExamineFile(BEntry& entry, char* buffer); - -private: - // Contains pointers to BDirectory objects. - BList* fDirectories; - - // The directory we are currently looking at. - BDirectory* fCurrentDir; - - // The ref number we are currently looking at. - int32 fCurrentRef; - + private: // The (escaped) search pattern. char* fPattern; // The directory or files to grep on. Model* fModel; + // The supplier of files to grep + FileIterator* fIterator; + // Our thread's ID. thread_id fThreadId; - + // Whether our thread must quit. volatile bool fMustQuit; }; diff --git a/src/apps/text_search/Jamfile b/src/apps/text_search/Jamfile index fa2d6c5b21..dcbf8975b2 100644 --- a/src/apps/text_search/Jamfile +++ b/src/apps/text_search/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src apps text_search ; SetSubDirSupportedPlatformsBeOSCompatible ; Application TextSearch : + FileIterator.cpp GrepApp.cpp GrepListView.cpp Grepper.cpp diff --git a/src/apps/text_search/Model.h b/src/apps/text_search/Model.h index 2c81457b0d..1a30d879aa 100644 --- a/src/apps/text_search/Model.h +++ b/src/apps/text_search/Model.h @@ -32,12 +32,7 @@ #include #include - -#define PREFS_FILE "TrackerGrepSettings" -#define HISTORY_LIMIT 20 - -#define TRACKER_SIGNATURE "application/x-vnd.Be-TRAK" -#define PE_SIGNATURE "application/x-vnd.beunited.pe" +#include "GlobalDefs.h" enum { @@ -89,6 +84,7 @@ public: void AddToHistory(const char* text); void FillHistoryMenu(BMenu* menu); +public: // The directory we were invoked from. entry_ref fDirectory;