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 35 using 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 43 InitialIterator::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 64 InitialIterator::~InitialIterator() 65 { 66 for (int32 i = fDirectories.CountItems() - 1; i >= 0; i--) 67 delete (BDirectory*)fDirectories.ItemAt(i); 68 } 69 70 71 bool 72 InitialIterator::IsValid() const 73 { 74 return fCurrentDir != NULL; 75 } 76 77 78 bool 79 InitialIterator::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 112 bool 113 InitialIterator::NotifyNegatives() const 114 { 115 return false; 116 } 117 118 119 bool 120 InitialIterator::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 147 bool 148 InitialIterator::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 168 bool 169 InitialIterator::_GetNextEntry(BEntry& entry) 170 { 171 if (fDirectories.CountItems() == 1) 172 return GetTopEntry(entry); 173 else 174 return _GetSubEntry(entry); 175 } 176 177 178 bool 179 InitialIterator::_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 198 void 199 InitialIterator::_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