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
18using 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
26InitialIterator::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
47InitialIterator::~InitialIterator()
48{
49	for (int32 i = fDirectories.CountItems() - 1; i >= 0; i--)
50		delete (BDirectory*)fDirectories.ItemAt(i);
51}
52
53
54bool
55InitialIterator::IsValid() const
56{
57	return fCurrentDir != NULL;
58}
59
60
61bool
62InitialIterator::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
95bool
96InitialIterator::NotifyNegatives() const
97{
98	return false;
99}
100
101
102bool
103InitialIterator::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
130bool
131InitialIterator::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
151bool
152InitialIterator::_GetNextEntry(BEntry& entry)
153{
154	if (fDirectories.CountItems() == 1)
155		return GetTopEntry(entry);
156	else
157		return _GetSubEntry(entry);
158}
159
160
161bool
162InitialIterator::_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
181void
182InitialIterator::_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