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