xref: /haiku/src/apps/text_search/InitialIterator.cpp (revision 899e0ef82b5624ace2ccfa5f5a58c8ebee54aaef)
1 /*
2  * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>
3  * Copyright (c) 1998-2007 Matthijs Hollemans
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 #include "InitialIterator.h"
8 
9 #include <new>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <Directory.h>
15 
16 #include "Model.h"
17 
18 using std::nothrow;
19 
20 // TODO: stippi: Check if this is a the best place to maintain a global
21 // list of files and folders for node monitoring. It should probably monitor
22 // every file that was grepped, as well as every visited (sub) folder.
23 // For the moment I don't know the life cycle of the InitialIterator object.
24 
25 
26 InitialIterator::InitialIterator(const Model* model)
27 	: FileIterator(),
28 	  fDirectories(32),
29 	  fCurrentDir(new (nothrow) BDirectory(&model->fDirectory)),
30 	  fCurrentRef(0),
31 
32 	  fSelectedFiles(model->fSelectedFiles),
33 
34 	  fRecurseDirs(model->fRecurseDirs),
35 	  fRecurseLinks(model->fRecurseLinks),
36 	  fSkipDotDirs(model->fSkipDotDirs),
37 	  fTextOnly(model->fTextOnly)
38 {
39 	if (!fCurrentDir || !fDirectories.AddItem(fCurrentDir)) {
40 		// init error
41 		delete fCurrentDir;
42 		fCurrentDir = NULL;
43 	}
44 }
45 
46 
47 InitialIterator::~InitialIterator()
48 {
49 	for (int32 i = fDirectories.CountItems() - 1; i >= 0; i--)
50 		delete (BDirectory*)fDirectories.ItemAt(i);
51 }
52 
53 
54 bool
55 InitialIterator::IsValid() const
56 {
57 	return fCurrentDir != NULL;
58 }
59 
60 
61 bool
62 InitialIterator::GetNextName(char* buffer)
63 {
64 	BEntry entry;
65 	struct stat fileStat;
66 
67 	while (true) {
68 		// Traverse the directory to get a new BEntry.
69 		// _GetNextEntry returns false if there are no
70 		// more entries, and we exit the loop.
71 
72 		if (!_GetNextEntry(entry))
73 			return false;
74 
75 		// If the entry is a subdir, then add it to the
76 		// list of directories and continue the loop.
77 		// If the entry is a file and we can grep it
78 		// (i.e. it is a text file), then we're done
79 		// here. Otherwise, continue with the next entry.
80 
81 		if (entry.GetStat(&fileStat) == B_OK) {
82 			if (S_ISDIR(fileStat.st_mode)) {
83 				// subdir
84 				_ExamineSubdir(entry);
85 			} else {
86 				// file or a (non-traversed) symbolic link
87 				if (_ExamineFile(entry, buffer, fTextOnly))
88 					return true;
89 			}
90 		}
91 	}
92 }
93 
94 
95 bool
96 InitialIterator::NotifyNegatives() const
97 {
98 	return false;
99 }
100 
101 
102 bool
103 InitialIterator::GetTopEntry(BEntry& entry)
104 {
105 	// If the user selected one or more files, we must look
106 	// at the "refs" inside the message that was passed into
107 	// our add-on's process_refs(). If the user didn't select
108 	// any files, we will simply read all the entries from the
109 	// current working directory.
110 
111 	entry_ref fileRef;
112 
113 	if (fSelectedFiles.FindRef("refs", fCurrentRef, &fileRef) == B_OK) {
114 		entry.SetTo(&fileRef, fRecurseLinks);
115 		++fCurrentRef;
116 		return true;
117 	} else if (fCurrentRef > 0) {
118 		// when we get here, we have processed
119 		// all the refs from the message
120 		return false;
121 	} else if (fCurrentDir != NULL) {
122 		// examine the whole directory
123 		return fCurrentDir->GetNextEntry(&entry, fRecurseLinks) == B_OK;
124 	}
125 
126 	return false;
127 }
128 
129 
130 bool
131 InitialIterator::FollowSubdir(BEntry& entry) const
132 {
133 	if (!fRecurseDirs)
134 		return false;
135 
136 	if (fSkipDotDirs) {
137 		char nameBuf[B_FILE_NAME_LENGTH];
138 		if (entry.GetName(nameBuf) == B_OK) {
139 			if (*nameBuf == '.')
140 				return false;
141 		}
142 	}
143 
144 	return true;
145 }
146 
147 
148 // #pragma mark - private
149 
150 
151 bool
152 InitialIterator::_GetNextEntry(BEntry& entry)
153 {
154 	if (fDirectories.CountItems() == 1)
155 		return GetTopEntry(entry);
156 	else
157 		return _GetSubEntry(entry);
158 }
159 
160 
161 bool
162 InitialIterator::_GetSubEntry(BEntry& entry)
163 {
164 	if (!fCurrentDir)
165 		return false;
166 
167 	if (fCurrentDir->GetNextEntry(&entry, fRecurseLinks) == B_OK)
168 		return true;
169 
170 	// If we get here, there are no more entries in
171 	// this subdir, so return to the parent directory.
172 
173 	fDirectories.RemoveItem(fCurrentDir);
174 	delete fCurrentDir;
175 	fCurrentDir = (BDirectory*)fDirectories.LastItem();
176 
177 	return _GetNextEntry(entry);
178 }
179 
180 
181 void
182 InitialIterator::_ExamineSubdir(BEntry& entry)
183 {
184 	if (!FollowSubdir(entry))
185 		return;
186 
187 	BDirectory* dir = new (nothrow) BDirectory(&entry);
188 	if (dir == NULL || dir->InitCheck() != B_OK || !fDirectories.AddItem(dir)) {
189 		// clean up
190 		delete dir;
191 		return;
192 	}
193 
194 	fCurrentDir = dir;
195 }
196