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
This commit is contained in:
Stephan Aßmus
2008-08-03 10:45:51 +00:00
parent 5054db1859
commit 1fffad3f43
8 changed files with 348 additions and 221 deletions
+225
View File
@@ -0,0 +1,225 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>
* 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 <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Directory.h>
#include <NodeInfo.h>
#include <Path.h>
#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;
}
+76
View File
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2008 Stephan Aßmus <[email protected]>
* 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 <List.h>
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
+8
View File
@@ -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
+17 -6
View File
@@ -38,6 +38,7 @@
#include <String.h>
#include <UTF8.h>
#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();
+10 -178
View File
@@ -33,6 +33,8 @@
#include <Path.h>
#include <UTF8.h>
#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<BDirectory*>(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;
}
+9 -31
View File
@@ -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;
};
+1
View File
@@ -3,6 +3,7 @@ SubDir HAIKU_TOP src apps text_search ;
SetSubDirSupportedPlatformsBeOSCompatible ;
Application TextSearch :
FileIterator.cpp
GrepApp.cpp
GrepListView.cpp
Grepper.cpp
+2 -6
View File
@@ -32,12 +32,7 @@
#include <Rect.h>
#include <String.h>
#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;