xref: /haiku/src/apps/text_search/FileIterator.cpp (revision 1fffad3f437a224769ce48659db1eaadfb5deeeb)
1*1fffad3fSStephan Aßmus /*
2*1fffad3fSStephan Aßmus  * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>
3*1fffad3fSStephan Aßmus  * Copyright (c) 1998-2007 Matthijs Hollemans
4*1fffad3fSStephan Aßmus  *
5*1fffad3fSStephan Aßmus  * Permission is hereby granted, free of charge, to any person obtaining a
6*1fffad3fSStephan Aßmus  * copy of this software and associated documentation files (the "Software"),
7*1fffad3fSStephan Aßmus  * to deal in the Software without restriction, including without limitation
8*1fffad3fSStephan Aßmus  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9*1fffad3fSStephan Aßmus  * and/or sell copies of the Software, and to permit persons to whom the
10*1fffad3fSStephan Aßmus  * Software is furnished to do so, subject to the following conditions:
11*1fffad3fSStephan Aßmus  *
12*1fffad3fSStephan Aßmus  * The above copyright notice and this permission notice shall be included in
13*1fffad3fSStephan Aßmus  * all copies or substantial portions of the Software.
14*1fffad3fSStephan Aßmus  *
15*1fffad3fSStephan Aßmus  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*1fffad3fSStephan Aßmus  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*1fffad3fSStephan Aßmus  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18*1fffad3fSStephan Aßmus  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*1fffad3fSStephan Aßmus  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*1fffad3fSStephan Aßmus  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21*1fffad3fSStephan Aßmus  * DEALINGS IN THE SOFTWARE.
22*1fffad3fSStephan Aßmus  */
23*1fffad3fSStephan Aßmus 
24*1fffad3fSStephan Aßmus #include "FileIterator.h"
25*1fffad3fSStephan Aßmus 
26*1fffad3fSStephan Aßmus #include <new>
27*1fffad3fSStephan Aßmus #include <stdio.h>
28*1fffad3fSStephan Aßmus #include <stdlib.h>
29*1fffad3fSStephan Aßmus #include <string.h>
30*1fffad3fSStephan Aßmus 
31*1fffad3fSStephan Aßmus #include <Directory.h>
32*1fffad3fSStephan Aßmus #include <NodeInfo.h>
33*1fffad3fSStephan Aßmus #include <Path.h>
34*1fffad3fSStephan Aßmus 
35*1fffad3fSStephan Aßmus #include "Model.h"
36*1fffad3fSStephan Aßmus 
37*1fffad3fSStephan Aßmus using std::nothrow;
38*1fffad3fSStephan Aßmus 
39*1fffad3fSStephan Aßmus // TODO: stippi: Check if this is a the best place to maintain a global
40*1fffad3fSStephan Aßmus // list of files and folders for node monitoring. It should probably monitor
41*1fffad3fSStephan Aßmus // every file that was grepped, as well as every visited (sub) folder.
42*1fffad3fSStephan Aßmus // For the moment I don't know the life cycle of the FileIterator object.
43*1fffad3fSStephan Aßmus 
44*1fffad3fSStephan Aßmus 
45*1fffad3fSStephan Aßmus FileIterator::FileIterator(Model* model)
46*1fffad3fSStephan Aßmus 	: fDirectories(10),
47*1fffad3fSStephan Aßmus 	  fCurrentDir(new (nothrow) BDirectory(&model->fDirectory)),
48*1fffad3fSStephan Aßmus 	  fCurrentRef(0),
49*1fffad3fSStephan Aßmus 	  fModel(model)
50*1fffad3fSStephan Aßmus {
51*1fffad3fSStephan Aßmus 	if (!fCurrentDir || !fDirectories.AddItem(fCurrentDir)) {
52*1fffad3fSStephan Aßmus 		// init error
53*1fffad3fSStephan Aßmus 		delete fCurrentDir;
54*1fffad3fSStephan Aßmus 		fCurrentDir = NULL;
55*1fffad3fSStephan Aßmus 	}
56*1fffad3fSStephan Aßmus }
57*1fffad3fSStephan Aßmus 
58*1fffad3fSStephan Aßmus 
59*1fffad3fSStephan Aßmus FileIterator::~FileIterator()
60*1fffad3fSStephan Aßmus {
61*1fffad3fSStephan Aßmus 	for (int32 i = fDirectories.CountItems() - 1; i >= 0; i--)
62*1fffad3fSStephan Aßmus 		delete (BDirectory*)fDirectories.ItemAt(i);
63*1fffad3fSStephan Aßmus }
64*1fffad3fSStephan Aßmus 
65*1fffad3fSStephan Aßmus 
66*1fffad3fSStephan Aßmus bool
67*1fffad3fSStephan Aßmus FileIterator::IsValid() const
68*1fffad3fSStephan Aßmus {
69*1fffad3fSStephan Aßmus 	return fCurrentDir != NULL;
70*1fffad3fSStephan Aßmus }
71*1fffad3fSStephan Aßmus 
72*1fffad3fSStephan Aßmus 
73*1fffad3fSStephan Aßmus bool
74*1fffad3fSStephan Aßmus FileIterator::GetNextName(char* buffer)
75*1fffad3fSStephan Aßmus {
76*1fffad3fSStephan Aßmus 	BEntry entry;
77*1fffad3fSStephan Aßmus 	struct stat fileStat;
78*1fffad3fSStephan Aßmus 
79*1fffad3fSStephan Aßmus 	while (true) {
80*1fffad3fSStephan Aßmus 		// Traverse the directory to get a new BEntry.
81*1fffad3fSStephan Aßmus 		// _GetNextEntry returns false if there are no
82*1fffad3fSStephan Aßmus 		// more entries, and we exit the loop.
83*1fffad3fSStephan Aßmus 
84*1fffad3fSStephan Aßmus 		if (!_GetNextEntry(entry))
85*1fffad3fSStephan Aßmus 			return false;
86*1fffad3fSStephan Aßmus 
87*1fffad3fSStephan Aßmus 		// If the entry is a subdir, then add it to the
88*1fffad3fSStephan Aßmus 		// list of directories and continue the loop.
89*1fffad3fSStephan Aßmus 		// If the entry is a file and we can grep it
90*1fffad3fSStephan Aßmus 		// (i.e. it is a text file), then we're done
91*1fffad3fSStephan Aßmus 		// here. Otherwise, continue with the next entry.
92*1fffad3fSStephan Aßmus 
93*1fffad3fSStephan Aßmus 		if (entry.GetStat(&fileStat) == B_OK) {
94*1fffad3fSStephan Aßmus 			if (S_ISDIR(fileStat.st_mode)) {
95*1fffad3fSStephan Aßmus 				// subdir
96*1fffad3fSStephan Aßmus 				_ExamineSubdir(entry);
97*1fffad3fSStephan Aßmus 			} else {
98*1fffad3fSStephan Aßmus 				// file or a (non-traversed) symbolic link
99*1fffad3fSStephan Aßmus 				if (_ExamineFile(entry, buffer))
100*1fffad3fSStephan Aßmus 					return true;
101*1fffad3fSStephan Aßmus 			}
102*1fffad3fSStephan Aßmus 		}
103*1fffad3fSStephan Aßmus 	}
104*1fffad3fSStephan Aßmus }
105*1fffad3fSStephan Aßmus 
106*1fffad3fSStephan Aßmus 
107*1fffad3fSStephan Aßmus // #pragma mark - private
108*1fffad3fSStephan Aßmus 
109*1fffad3fSStephan Aßmus 
110*1fffad3fSStephan Aßmus bool
111*1fffad3fSStephan Aßmus FileIterator::_GetNextEntry(BEntry& entry)
112*1fffad3fSStephan Aßmus {
113*1fffad3fSStephan Aßmus 	if (fDirectories.CountItems() == 1)
114*1fffad3fSStephan Aßmus 		return _GetTopEntry(entry);
115*1fffad3fSStephan Aßmus 	else
116*1fffad3fSStephan Aßmus 		return _GetSubEntry(entry);
117*1fffad3fSStephan Aßmus }
118*1fffad3fSStephan Aßmus 
119*1fffad3fSStephan Aßmus 
120*1fffad3fSStephan Aßmus bool
121*1fffad3fSStephan Aßmus FileIterator::_GetTopEntry(BEntry& entry)
122*1fffad3fSStephan Aßmus {
123*1fffad3fSStephan Aßmus 	// If the user selected one or more files, we must look
124*1fffad3fSStephan Aßmus 	// at the "refs" inside the message that was passed into
125*1fffad3fSStephan Aßmus 	// our add-on's process_refs(). If the user didn't select
126*1fffad3fSStephan Aßmus 	// any files, we will simply read all the entries from the
127*1fffad3fSStephan Aßmus 	// current working directory.
128*1fffad3fSStephan Aßmus 
129*1fffad3fSStephan Aßmus 	entry_ref fileRef;
130*1fffad3fSStephan Aßmus 
131*1fffad3fSStephan Aßmus 	if (fModel->fSelectedFiles.FindRef("refs", fCurrentRef, &fileRef) == B_OK) {
132*1fffad3fSStephan Aßmus 		entry.SetTo(&fileRef, fModel->fRecurseLinks);
133*1fffad3fSStephan Aßmus 		++fCurrentRef;
134*1fffad3fSStephan Aßmus 		return true;
135*1fffad3fSStephan Aßmus 	} else if (fCurrentRef > 0) {
136*1fffad3fSStephan Aßmus 		// when we get here, we have processed
137*1fffad3fSStephan Aßmus 		// all the refs from the message
138*1fffad3fSStephan Aßmus 		return false;
139*1fffad3fSStephan Aßmus 	} else if (fCurrentDir != NULL) {
140*1fffad3fSStephan Aßmus 		// examine the whole directory
141*1fffad3fSStephan Aßmus 		return fCurrentDir->GetNextEntry(&entry,
142*1fffad3fSStephan Aßmus 			fModel->fRecurseLinks) == B_OK;
143*1fffad3fSStephan Aßmus 	}
144*1fffad3fSStephan Aßmus 
145*1fffad3fSStephan Aßmus 	return false;
146*1fffad3fSStephan Aßmus }
147*1fffad3fSStephan Aßmus 
148*1fffad3fSStephan Aßmus 
149*1fffad3fSStephan Aßmus bool
150*1fffad3fSStephan Aßmus FileIterator::_GetSubEntry(BEntry& entry)
151*1fffad3fSStephan Aßmus {
152*1fffad3fSStephan Aßmus 	if (!fCurrentDir)
153*1fffad3fSStephan Aßmus 		return false;
154*1fffad3fSStephan Aßmus 
155*1fffad3fSStephan Aßmus 	if (fCurrentDir->GetNextEntry(&entry, fModel->fRecurseLinks) == B_OK)
156*1fffad3fSStephan Aßmus 		return true;
157*1fffad3fSStephan Aßmus 
158*1fffad3fSStephan Aßmus 	// If we get here, there are no more entries in
159*1fffad3fSStephan Aßmus 	// this subdir, so return to the parent directory.
160*1fffad3fSStephan Aßmus 
161*1fffad3fSStephan Aßmus 	fDirectories.RemoveItem(fCurrentDir);
162*1fffad3fSStephan Aßmus 	delete fCurrentDir;
163*1fffad3fSStephan Aßmus 	fCurrentDir = (BDirectory*)fDirectories.LastItem();
164*1fffad3fSStephan Aßmus 
165*1fffad3fSStephan Aßmus 	return _GetNextEntry(entry);
166*1fffad3fSStephan Aßmus }
167*1fffad3fSStephan Aßmus 
168*1fffad3fSStephan Aßmus 
169*1fffad3fSStephan Aßmus void
170*1fffad3fSStephan Aßmus FileIterator::_ExamineSubdir(BEntry& entry)
171*1fffad3fSStephan Aßmus {
172*1fffad3fSStephan Aßmus 	if (!fModel->fRecurseDirs)
173*1fffad3fSStephan Aßmus 		return;
174*1fffad3fSStephan Aßmus 
175*1fffad3fSStephan Aßmus 	if (fModel->fSkipDotDirs) {
176*1fffad3fSStephan Aßmus 		char nameBuf[B_FILE_NAME_LENGTH];
177*1fffad3fSStephan Aßmus 		if (entry.GetName(nameBuf) == B_OK) {
178*1fffad3fSStephan Aßmus 			if (*nameBuf == '.')
179*1fffad3fSStephan Aßmus 				return;
180*1fffad3fSStephan Aßmus 		}
181*1fffad3fSStephan Aßmus 	}
182*1fffad3fSStephan Aßmus 
183*1fffad3fSStephan Aßmus 	BDirectory* dir = new (nothrow) BDirectory(&entry);
184*1fffad3fSStephan Aßmus 	if (dir == NULL || dir->InitCheck() != B_OK
185*1fffad3fSStephan Aßmus 		|| !fDirectories.AddItem(dir)) {
186*1fffad3fSStephan Aßmus 		// clean up
187*1fffad3fSStephan Aßmus 		delete dir;
188*1fffad3fSStephan Aßmus 		return;
189*1fffad3fSStephan Aßmus 	}
190*1fffad3fSStephan Aßmus 
191*1fffad3fSStephan Aßmus 	fCurrentDir = dir;
192*1fffad3fSStephan Aßmus }
193*1fffad3fSStephan Aßmus 
194*1fffad3fSStephan Aßmus 
195*1fffad3fSStephan Aßmus bool
196*1fffad3fSStephan Aßmus FileIterator::_ExamineFile(BEntry& entry, char* buffer)
197*1fffad3fSStephan Aßmus {
198*1fffad3fSStephan Aßmus 	BPath path;
199*1fffad3fSStephan Aßmus 	if (entry.GetPath(&path) != B_OK)
200*1fffad3fSStephan Aßmus 		return false;
201*1fffad3fSStephan Aßmus 
202*1fffad3fSStephan Aßmus 	strcpy(buffer, path.Path());
203*1fffad3fSStephan Aßmus 
204*1fffad3fSStephan Aßmus 	if (!fModel->fTextOnly)
205*1fffad3fSStephan Aßmus 		return true;
206*1fffad3fSStephan Aßmus 
207*1fffad3fSStephan Aßmus 	BNode node(&entry);
208*1fffad3fSStephan Aßmus 	BNodeInfo nodeInfo(&node);
209*1fffad3fSStephan Aßmus 	char mimeTypeString[B_MIME_TYPE_LENGTH];
210*1fffad3fSStephan Aßmus 
211*1fffad3fSStephan Aßmus 	if (nodeInfo.GetType(mimeTypeString) == B_OK) {
212*1fffad3fSStephan Aßmus 		BMimeType mimeType(mimeTypeString);
213*1fffad3fSStephan Aßmus 		BMimeType superType;
214*1fffad3fSStephan Aßmus 
215*1fffad3fSStephan Aßmus 		if (mimeType.GetSupertype(&superType) == B_OK) {
216*1fffad3fSStephan Aßmus 			if (strcmp("text", superType.Type()) == 0
217*1fffad3fSStephan Aßmus 				|| strcmp("message", superType.Type()) == 0) {
218*1fffad3fSStephan Aßmus 				return true;
219*1fffad3fSStephan Aßmus 			}
220*1fffad3fSStephan Aßmus 		}
221*1fffad3fSStephan Aßmus 	}
222*1fffad3fSStephan Aßmus 
223*1fffad3fSStephan Aßmus 	return false;
224*1fffad3fSStephan Aßmus }
225*1fffad3fSStephan Aßmus 
226