diff --git a/src/apps/text_search/FileIterator.cpp b/src/apps/text_search/FileIterator.cpp index 9b1ac9bfe8..e3b0ddfcea 100644 --- a/src/apps/text_search/FileIterator.cpp +++ b/src/apps/text_search/FileIterator.cpp @@ -23,177 +23,25 @@ #include "FileIterator.h" -#include -#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) +FileIterator::FileIterator() { - 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) +FileIterator::_ExamineFile(BEntry& entry, char* buffer, bool textFilesOnly) { BPath path; if (entry.GetPath(&path) != B_OK) @@ -201,22 +49,23 @@ FileIterator::_ExamineFile(BEntry& entry, char* buffer) strcpy(buffer, path.Path()); - if (!fModel->fTextOnly) + if (!textFilesOnly) 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 (nodeInfo.GetType(mimeTypeString) != B_OK) + return false; - if (mimeType.GetSupertype(&superType) == B_OK) { - if (strcmp("text", superType.Type()) == 0 - || strcmp("message", superType.Type()) == 0) { - return true; - } + BMimeType mimeType(mimeTypeString); + BMimeType superType; + + if (mimeType.GetSupertype(&superType) == B_OK) { + if (strcmp("text", superType.Type()) == 0 + || strcmp("message", superType.Type()) == 0) { + return true; } } diff --git a/src/apps/text_search/FileIterator.h b/src/apps/text_search/FileIterator.h index abeb44aade..ebda8c48bf 100644 --- a/src/apps/text_search/FileIterator.h +++ b/src/apps/text_search/FileIterator.h @@ -23,54 +23,24 @@ #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); + FileIterator(); virtual ~FileIterator(); - virtual bool IsValid() const; + virtual bool IsValid() const = 0; // 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); + virtual bool GetNextName(char* buffer) = 0; +protected: // 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; + bool _ExamineFile(BEntry& entry, char* buffer, + bool textFilesOnly); }; #endif // FILE_ITERATOR_H diff --git a/src/apps/text_search/FolderIterator.cpp b/src/apps/text_search/FolderIterator.cpp new file mode 100644 index 0000000000..d29a0082b8 --- /dev/null +++ b/src/apps/text_search/FolderIterator.cpp @@ -0,0 +1,195 @@ +/* + * 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 "FolderIterator.h" + +#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 FolderIterator object. + + +FolderIterator::FolderIterator(const Model* model) + : FileIterator(), + fDirectories(10), + fCurrentDir(new (nothrow) BDirectory(&model->fDirectory)), + fCurrentRef(0), + + fSelectedFiles(model->fSelectedFiles), + + fRecurseDirs(model->fRecurseDirs), + fRecurseLinks(model->fRecurseLinks), + fSkipDotDirs(model->fSkipDotDirs), + fTextOnly(model->fTextOnly) +{ + if (!fCurrentDir || !fDirectories.AddItem(fCurrentDir)) { + // init error + delete fCurrentDir; + fCurrentDir = NULL; + } +} + + +FolderIterator::~FolderIterator() +{ + for (int32 i = fDirectories.CountItems() - 1; i >= 0; i--) + delete (BDirectory*)fDirectories.ItemAt(i); +} + + +bool +FolderIterator::IsValid() const +{ + return fCurrentDir != NULL; +} + + +bool +FolderIterator::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, fTextOnly)) + return true; + } + } + } +} + + +// #pragma mark - private + + +bool +FolderIterator::_GetNextEntry(BEntry& entry) +{ + if (fDirectories.CountItems() == 1) + return _GetTopEntry(entry); + else + return _GetSubEntry(entry); +} + + +bool +FolderIterator::_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 (fSelectedFiles.FindRef("refs", fCurrentRef, &fileRef) == B_OK) { + entry.SetTo(&fileRef, 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, fRecurseLinks) == B_OK; + } + + return false; +} + + +bool +FolderIterator::_GetSubEntry(BEntry& entry) +{ + if (!fCurrentDir) + return false; + + if (fCurrentDir->GetNextEntry(&entry, 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 +FolderIterator::_ExamineSubdir(BEntry& entry) +{ + if (!fRecurseDirs) + return; + + if (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; +} diff --git a/src/apps/text_search/FolderIterator.h b/src/apps/text_search/FolderIterator.h new file mode 100644 index 0000000000..82de89d767 --- /dev/null +++ b/src/apps/text_search/FolderIterator.h @@ -0,0 +1,86 @@ +/* + * 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 FOLDER_ITERATOR_H +#define FOLDER_ITERATOR_H + +#include +#include + +#include "FileIterator.h" + +class BEntry; +class BDirectory; +class Model; + +// TODO: split into Folder and MessageFileIterator (_GetTopEntry) +// This code either has an original folder to start iterating from, +// or it uses the refs in the message that contains the selected poses +// from the Tracker window that invoked us. But I think if folders are +// among those poses, it will then use the same code for examining sub +// folders. + +// Provides an interface to retrieve the next file that should be grepped +// for the search string. +class FolderIterator : public FileIterator { +public: + FolderIterator(const Model* model); + virtual ~FolderIterator(); + + 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); + +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 current settings + BMessage fSelectedFiles; + + bool fRecurseDirs : 1; + bool fRecurseLinks : 1; + bool fSkipDotDirs : 1; + bool fTextOnly : 1; +}; + +#endif // FOLDER_ITERATOR_H diff --git a/src/apps/text_search/GrepWindow.cpp b/src/apps/text_search/GrepWindow.cpp index 4e6b575b57..1669e957e6 100644 --- a/src/apps/text_search/GrepWindow.cpp +++ b/src/apps/text_search/GrepWindow.cpp @@ -38,7 +38,7 @@ #include #include -#include "FileIterator.h" +#include "FolderIterator.h" #include "GlobalDefs.h" #include "Grepper.h" #include "Translation.h" @@ -98,7 +98,6 @@ GrepWindow::GrepWindow(BMessage* message) fModel->fDirectory = directory; fModel->fSelectedFiles = *message; - fModel->fTarget = this; _SetWindowTitle(); _CreateMenus(); @@ -706,9 +705,9 @@ GrepWindow::_OnStartCancel() fOldPattern = fSearchText->Text(); - FileIterator* iterator = new (nothrow) FileIterator(fModel); + FileIterator* iterator = new (nothrow) FolderIterator(fModel); fGrepper = new (nothrow) Grepper(fOldPattern.String(), fModel, - iterator); + this, iterator); if (fGrepper != NULL && fGrepper->IsValid()) fGrepper->Start(); else { diff --git a/src/apps/text_search/Grepper.cpp b/src/apps/text_search/Grepper.cpp index 9b9ba805a9..ba730d6ff5 100644 --- a/src/apps/text_search/Grepper.cpp +++ b/src/apps/text_search/Grepper.cpp @@ -34,6 +34,7 @@ #include #include "FileIterator.h" +#include "Model.h" using std::nothrow; @@ -90,16 +91,20 @@ strdup_from_utf8(uint32 encode, const char* src, int32 length) } -Grepper::Grepper(const char* pattern, Model* model, FileIterator* iterator) +Grepper::Grepper(const char* pattern, const Model* model, + const BHandler* target, FileIterator* iterator) : fPattern(NULL), - fModel(model), + fTarget(target), + fEscapeText(model->fEscapeText), + fCaseSensitive(model->fCaseSensitive), + fEncoding(model->fEncoding), + fIterator(iterator), fThreadId(-1), fMustQuit(false) { - if (fModel->fEncoding) { - char *src = strdup_from_utf8(fModel->fEncoding, pattern, - strlen(pattern)); + if (fEncoding > 0) { + char* src = strdup_from_utf8(fEncoding, pattern, strlen(pattern)); _SetPattern(src); free(src); } else @@ -120,7 +125,7 @@ Grepper::IsValid() const { if (fIterator == NULL || !fIterator->IsValid()) return false; - return fPattern != NULL && fModel != NULL; + return fPattern != NULL; } @@ -178,7 +183,7 @@ Grepper::_GrepperThread() message.MakeEmpty(); message.what = MSG_REPORT_FILE_NAME; message.AddString("filename", fileName); - fModel->fTarget->PostMessage(&message); + fTarget.SendMessage(&message); message.MakeEmpty(); message.what = MSG_REPORT_RESULT; @@ -196,13 +201,12 @@ Grepper::_GrepperThread() message.MakeEmpty(); message.what = MSG_REPORT_ERROR; message.AddString("error", tempString); - fModel->fTarget->PostMessage(&message); + fTarget.SendMessage(&message); continue; } sprintf(command, "grep -hn %s %s \"%s\" > \"%s\"", - fModel->fCaseSensitive ? "" : "-i", fPattern, fileName, - tempFile.Path()); + fCaseSensitive ? "" : "-i", fPattern, fileName, tempFile.Path()); int res = system(command); @@ -211,9 +215,9 @@ Grepper::_GrepperThread() if (results != NULL) { while (fgets(tempString, B_PATH_NAME_LENGTH, results) != 0) { - if (fModel->fEncoding) { - char *tempdup = strdup_to_utf8(fModel->fEncoding, - tempString, strlen(tempString)); + if (fEncoding > 0) { + char* tempdup = strdup_to_utf8(fEncoding, tempString, + strlen(tempString)); message.AddString("text", tempdup); free(tempdup); } else @@ -221,7 +225,7 @@ Grepper::_GrepperThread() } if (message.HasString("text")) - fModel->fTarget->PostMessage(&message); + fTarget.SendMessage(&message); fclose(results); continue; @@ -233,7 +237,7 @@ Grepper::_GrepperThread() message.MakeEmpty(); message.what = MSG_REPORT_ERROR; message.AddString("error", tempString); - fModel->fTarget->PostMessage(&message); + fTarget.SendMessage(&message); } // We wait with removing the temporary file until after the @@ -244,7 +248,7 @@ Grepper::_GrepperThread() message.MakeEmpty(); message.what = MSG_SEARCH_FINISHED; - fModel->fTarget->PostMessage(&message); + fTarget.SendMessage(&message); return 0; } @@ -256,7 +260,7 @@ Grepper::_SetPattern(const char* src) if (src == NULL) return; - if (!fModel->fEscapeText) { + if (!fEscapeText) { fPattern = strdup(src); return; } diff --git a/src/apps/text_search/Grepper.h b/src/apps/text_search/Grepper.h index a8883b7825..23d04745bc 100644 --- a/src/apps/text_search/Grepper.h +++ b/src/apps/text_search/Grepper.h @@ -22,14 +22,16 @@ #ifndef GREPPER_H #define GREPPER_H -#include "Model.h" +#include class FileIterator; +class Model; // Executes "grep" in a background thread. class Grepper { public: - Grepper(const char* pattern, Model* model, + Grepper(const char* pattern, const Model* model, + const BHandler* target, FileIterator* iterator); virtual ~Grepper(); @@ -56,8 +58,11 @@ private: // The (escaped) search pattern. char* fPattern; - // The directory or files to grep on. - Model* fModel; + // The settings from the model. + BMessenger fTarget; + bool fEscapeText : 1; + bool fCaseSensitive : 1; + uint32 fEncoding; // The supplier of files to grep FileIterator* fIterator; diff --git a/src/apps/text_search/Jamfile b/src/apps/text_search/Jamfile index dcbf8975b2..ab78d52797 100644 --- a/src/apps/text_search/Jamfile +++ b/src/apps/text_search/Jamfile @@ -4,6 +4,7 @@ SetSubDirSupportedPlatformsBeOSCompatible ; Application TextSearch : FileIterator.cpp + FolderIterator.cpp GrepApp.cpp GrepListView.cpp Grepper.cpp diff --git a/src/apps/text_search/Model.cpp b/src/apps/text_search/Model.cpp index b2eefaa86e..e65d7776b2 100644 --- a/src/apps/text_search/Model.cpp +++ b/src/apps/text_search/Model.cpp @@ -53,7 +53,6 @@ Model::Model() fFrame(100, 100, 500, 400), - fTarget(NULL), fState(STATE_IDLE), fFilePanelPath(""), diff --git a/src/apps/text_search/Model.h b/src/apps/text_search/Model.h index 1a30d879aa..f2a64275d6 100644 --- a/src/apps/text_search/Model.h +++ b/src/apps/text_search/Model.h @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -118,9 +117,6 @@ public: // The dimensions of the window. BRect fFrame; - // The looper that will receive notifications. - BLooper* fTarget; - // What are we doing. state_t fState;