sbDirectoryEnumerator.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4 //
5 // BEGIN SONGBIRD GPL
6 //
7 // This file is part of the Songbird web player.
8 //
9 // Copyright(c) 2005-2009 POTI, Inc.
10 // http://songbirdnest.com
11 //
12 // This file may be licensed under the terms of of the
13 // GNU General Public License Version 2 (the "GPL").
14 //
15 // Software distributed under the License is distributed
16 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
17 // express or implied. See the GPL for the specific language
18 // governing rights and limitations.
19 //
20 // You should have received a copy of the GPL along with this
21 // program. If not, go to http://www.gnu.org/licenses/gpl.html
22 // or write to the Free Software Foundation, Inc.,
23 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 //
25 // END SONGBIRD GPL
26 //
27 */
28 
29 //------------------------------------------------------------------------------
30 //------------------------------------------------------------------------------
31 //
32 // Songbird directory enumerator.
33 //
34 //------------------------------------------------------------------------------
35 //------------------------------------------------------------------------------
36 
42 //------------------------------------------------------------------------------
43 //
44 // Songbird directory enumerator imported services.
45 //
46 //------------------------------------------------------------------------------
47 
48 // Self imports.
49 #include "sbDirectoryEnumerator.h"
50 
51 // Platform includes
52 #if XP_WIN
53 #include <windows.h>
54 #endif
55 
56 // Mozilla imports.
57 #include <nsAutoLock.h>
58 #include <nsAutoPtr.h>
59 #include <nsComponentManagerUtils.h>
60 #include <nsIFile.h>
61 #include <nsILocalFile.h>
62 #include <prerr.h>
63 #include <prerror.h>
64 
65 //------------------------------------------------------------------------------
66 //
67 // Songbird directory enumerator nsISupports implementation.
68 //
69 //------------------------------------------------------------------------------
70 
72 
73 
74 //------------------------------------------------------------------------------
75 //
76 // Songbird directory enumerator sbIDirectoryEnumerator implementation.
77 //
78 //------------------------------------------------------------------------------
79 
80 
88 {
89 public:
94 
98  virtual ~sbDirectoryEnumeratorHelper();
99 
101  NS_DECL_NSISIMPLEENUMERATOR
102 
107  nsresult Init(nsIFile * aDirectory);
108 private:
109  nsCOMPtr<nsIFile> mDirectory;
110  nsString mNextPath;
111 #if XP_WIN
112  typedef HANDLE Directory;
113 #else
114  typedef PRDir * Directory;
115 #endif
116  Directory mDir;
117 
122  nsresult OpenDir(const nsAString & aPath);
123 
128  void CloseDir();
129 
135  nsresult ReadDir(nsAString & aPath);
136 };
137 
139 
141 #if XP_WIN
142  mDir(INVALID_HANDLE_VALUE)
143 #else
144  mDir(nsnull)
145 #endif
146 {
147 }
148 
150 {
151  CloseDir();
152 }
153 nsresult sbDirectoryEnumeratorHelper::Init(nsIFile * aDirectory)
154 {
155  NS_ENSURE_ARG_POINTER(aDirectory);
156 
157  nsresult rv;
158 
159  mDirectory = aDirectory;
160 
161  nsString path;
162  rv = aDirectory->GetPath(path);
163  NS_ENSURE_SUCCESS(rv, rv);
164 
165  rv = OpenDir(path);
166  NS_ENSURE_SUCCESS(rv, rv);
167 
168  return NS_OK;
169 }
170 
171 NS_IMETHODIMP
172 sbDirectoryEnumeratorHelper::HasMoreElements(PRBool *_retval NS_OUTPARAM)
173 {
174  NS_ENSURE_ARG_POINTER(_retval);
175 
176  *_retval = mNextPath.IsEmpty() ? PR_FALSE: PR_TRUE;
177  return NS_OK;
178 }
179 
180 NS_IMETHODIMP
181 sbDirectoryEnumeratorHelper::GetNext(nsISupports ** aEntry NS_OUTPARAM)
182 {
183  NS_ENSURE_ARG_POINTER(aEntry);
184 
185  if (mNextPath.IsEmpty()) {
186  return NS_ERROR_NOT_AVAILABLE;
187  }
188 
189  nsresult rv;
190 
191  nsCOMPtr<nsILocalFile> file =
192  do_CreateInstance("@mozilla.org/file/local;1", &rv);
193 
194  nsString path;
195  rv = mDirectory->GetPath(path);
196  NS_ENSURE_SUCCESS(rv, rv);
197 
198  rv = file->InitWithPath(path);
199  NS_ENSURE_SUCCESS(rv, rv);
200 
201  rv = file->Append(mNextPath);
202  NS_ENSURE_SUCCESS(rv, rv);
203 
204  *aEntry = file.get();
205  file.forget();
206 
207  // Prefetch the next entry if any, skipping . and ..
208  rv = ReadDir(mNextPath);
209  NS_ENSURE_SUCCESS(rv, rv);
210 
211  return NS_OK;
212 }
213 
214 template <class T>
215 inline
216 bool IsDotDir(T const * aPath)
217 {
218  // If "." or ".."
219  return aPath[0] == (T)'.' &&
220  ((aPath[1] == (T)'.' && aPath[2] == 0) || aPath[1] == 0);
221 }
222 
223 nsresult sbDirectoryEnumeratorHelper::OpenDir(const nsAString & aPath)
224 {
225  nsresult rv;
226 
227 #if XP_WIN
228  // Handle long paths
229  nsString path(aPath);
230  path.Cut(2, -1);
231  // If this is a relative path we can't prefix.
232  if (!path.EqualsLiteral(".") &&
233  !path.EqualsLiteral("..")) {
234  // If this is a unc file name we need the \\?\UNC prefix
235  if (path.EqualsLiteral("\\\\")) {
236  path = NS_LITERAL_STRING("\\\\?\\UNC\\");
237  path.Append(aPath.BeginReading() + 2, aPath.Length() - 2);
238  }
239  else {
240  // Standard DOS path prefix with \\?\ (prevent line continuation)
241  path = NS_LITERAL_STRING("\\\\?\\");
242  path.Append(aPath);
243  }
244  }
245  else {
246  path = aPath;
247  }
248  //If 'name' ends in a slash or backslash, do not append
249  //another backslash.
250  if (path.IsEmpty() ||
251  path[path.Length() - 1] == L'/' ||
252  path[path.Length() -1] == L'\\')
253  path.AppendLiteral("*");
254  else
255  path.AppendLiteral("\\*");
256 
257  PRUnichar *begin, *end;
258 
259  for (path.BeginWriting(&begin, &end); begin < end; ++begin) {
260  if (*begin == L'/') {
261  *begin = L'\\';
262  }
263  }
264 
265  WIN32_FIND_DATAW findData;
266  mDir = ::FindFirstFileW(path.BeginReading(), &findData);
267  NS_ENSURE_TRUE(mDir != INVALID_HANDLE_VALUE, NS_ERROR_FILE_NOT_FOUND);
268  if (IsDotDir(findData.cFileName)) {
269  rv = ReadDir(mNextPath);
270  NS_ENSURE_SUCCESS(rv, rv);
271  }
272  else {
273  mNextPath.Assign(findData.cFileName);
274  }
275 #else
276  mDir = PR_OpenDir(NS_ConvertUTF16toUTF8(aPath).BeginReading());
277  NS_ENSURE_TRUE(mDir, NS_ERROR_FILE_NOT_FOUND);
278 
279  rv = ReadDir(mNextPath);
280  NS_ENSURE_SUCCESS(rv, rv);
281 #endif
282 
283  return NS_OK;
284 }
285 void sbDirectoryEnumeratorHelper::CloseDir()
286 {
287 #if XP_WIN
288  if (mDir != INVALID_HANDLE_VALUE) {
289  ::FindClose(mDir);
290  mDir = INVALID_HANDLE_VALUE;
291  }
292 #else
293  if (mDir) {
294  PR_CloseDir(mDir);
295  mDir = nsnull;
296  }
297 #endif
298 }
299 
300 nsresult sbDirectoryEnumeratorHelper::ReadDir(nsAString & aPath)
301 {
302 #if XP_WIN
303  WIN32_FIND_DATAW findData;
304  BOOL succeeded = ::FindNextFileW(mDir, &findData);
305  while (succeeded && IsDotDir(findData.cFileName)) {
306  succeeded = ::FindNextFileW(mDir, &findData);
307  }
308  if (succeeded) {
309  aPath.Assign(nsString(findData.cFileName));
310  }
311  else
312  {
313  aPath.Truncate();
314  }
315 #else
316  PRDirEntry * entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
317  if (entry) {
318  aPath.Assign(NS_ConvertUTF8toUTF16(entry->name));
319  }
320  else {
321  aPath.Truncate();
322  NS_ENSURE_TRUE(PR_GetError() == PR_NO_MORE_FILES_ERROR,
323  NS_ERROR_UNEXPECTED);
324  }
325 #endif
326  return NS_OK;
327 }
328 
335 NS_IMETHODIMP
336 sbDirectoryEnumerator::Enumerate(nsIFile* aDirectory)
337 {
338  // Validate arguments and state.
339  NS_ENSURE_ARG_POINTER(aDirectory);
340  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
341 
342  // Function variables.
343  PRBool success;
344  nsresult rv;
345 
346  // Operate under the enumerator lock.
347  nsAutoLock autoLock(mEnumeratorLock);
348 
349  // Ensure directory exists and is a directory.
350  PRBool exists;
351  PRBool isDirectory;
352  rv = aDirectory->Exists(&exists);
353  NS_ENSURE_SUCCESS(rv, rv);
354  NS_ENSURE_TRUE(exists, NS_ERROR_FILE_NOT_FOUND);
355  rv = aDirectory->IsDirectory(&isDirectory);
356  NS_ENSURE_SUCCESS(rv, rv);
357  NS_ENSURE_TRUE(isDirectory, NS_ERROR_FILE_NOT_DIRECTORY);
358 
359  // Get the entries in the directory. If file not found is returned, the
360  // directory is empty.
361  nsRefPtr<sbDirectoryEnumeratorHelper> dirEnumerator =
363  NS_ENSURE_TRUE(dirEnumerator, NS_ERROR_OUT_OF_MEMORY);
364  rv = dirEnumerator->Init(aDirectory);
365 
366  // Initialize the entries enumeration stack.
367  mEntriesEnumStack.Clear();
368 
369  if (rv != NS_ERROR_FILE_NOT_FOUND) {
370  NS_ENSURE_SUCCESS(rv, rv);
371  success = mEntriesEnumStack.AppendObject(dirEnumerator);
372  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
373  }
374 
375  // Scan for the next file.
376  rv = ScanForNextFile();
377  NS_ENSURE_SUCCESS(rv, rv);
378 
379  return NS_OK;
380 }
381 
382 
389 NS_IMETHODIMP
390 sbDirectoryEnumerator::HasMoreElements(PRBool* aHasMoreElements)
391 {
392  // Validate arguments and state.
393  NS_ENSURE_ARG_POINTER(aHasMoreElements);
394  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
395 
396  // Operate under the enumerator lock.
397  nsAutoLock autoLock(mEnumeratorLock);
398 
399  // Return results.
400  *aHasMoreElements = (mEntriesEnumStack.Count() > 0);
401 
402  return NS_OK;
403 }
404 
405 
412 NS_IMETHODIMP
413 sbDirectoryEnumerator::GetNext(nsIFile** aFile)
414 {
415  // Validate arguments and state.
416  NS_ENSURE_ARG_POINTER(aFile);
417  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
418 
419  // Function variables.
420  nsresult rv;
421 
422  // Operate under the enumerator lock.
423  nsAutoLock autoLock(mEnumeratorLock);
424 
425  // Return the next file. Return error if no next file.
426  if (mNextFile)
427  mNextFile.forget(aFile);
428  else
429  return NS_ERROR_NOT_AVAILABLE;
430 
431  // Scan for the next file.
432  rv = ScanForNextFile();
433  NS_ENSURE_SUCCESS(rv, rv);
434 
435  return NS_OK;
436 }
437 
438 
439 //
440 // Getters/setters.
441 //
442 
449 NS_IMETHODIMP
450 sbDirectoryEnumerator::GetMaxDepth(PRUint32* aMaxDepth)
451 {
452  NS_ENSURE_ARG_POINTER(aMaxDepth);
453  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
454  nsAutoLock autoLock(mEnumeratorLock);
455  *aMaxDepth = mMaxDepth;
456  return NS_OK;
457 }
458 
459 NS_IMETHODIMP
460 sbDirectoryEnumerator::SetMaxDepth(PRUint32 aMaxDepth)
461 {
462  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
463  nsAutoLock autoLock(mEnumeratorLock);
464  mMaxDepth = aMaxDepth;
465  return NS_OK;
466 }
467 
468 
473 NS_IMETHODIMP
474 sbDirectoryEnumerator::GetDirectoriesOnly(PRBool* aDirectoriesOnly)
475 {
476  NS_ENSURE_ARG_POINTER(aDirectoriesOnly);
477  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
478  nsAutoLock autoLock(mEnumeratorLock);
479  *aDirectoriesOnly = mDirectoriesOnly;
480  return NS_OK;
481 }
482 
483 NS_IMETHODIMP
484 sbDirectoryEnumerator::SetDirectoriesOnly(PRBool aDirectoriesOnly)
485 {
486  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
487  nsAutoLock autoLock(mEnumeratorLock);
488  mDirectoriesOnly = aDirectoriesOnly;
489  return NS_OK;
490 }
491 
492 
497 NS_IMETHODIMP
498 sbDirectoryEnumerator::GetFilesOnly(PRBool* aFilesOnly)
499 {
500  NS_ENSURE_ARG_POINTER(aFilesOnly);
501  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
502  nsAutoLock autoLock(mEnumeratorLock);
503  *aFilesOnly = mFilesOnly;
504  return NS_OK;
505 }
506 
507 NS_IMETHODIMP
508 sbDirectoryEnumerator::SetFilesOnly(PRBool aFilesOnly)
509 {
510  NS_PRECONDITION(mIsInitialized, "Directory enumerator not initialized");
511  nsAutoLock autoLock(mEnumeratorLock);
512  mFilesOnly = aFilesOnly;
513  return NS_OK;
514 }
515 
516 
517 //------------------------------------------------------------------------------
518 //
519 // Songbird directory enumerator public services.
520 //
521 //------------------------------------------------------------------------------
522 
528  mIsInitialized(PR_FALSE),
529  mEnumeratorLock(nsnull),
530  mMaxDepth(0),
531  mDirectoriesOnly(PR_FALSE),
532  mFilesOnly(PR_FALSE)
533 {
534 }
535 
541 {
542  // Finalize the Songbird directory enumerator.
543  Finalize();
544 }
545 
546 
551 nsresult
553 {
554  // Do nothing if already initialized.
555  if (mIsInitialized)
556  return NS_OK;
557 
558  // Create the directory enumerator lock.
559  mEnumeratorLock =
560  nsAutoLock::NewLock("sbDirectoryEnumerator.mEnumeratorLock");
561  NS_ENSURE_TRUE(mEnumeratorLock, NS_ERROR_OUT_OF_MEMORY);
562 
563  // Indicate that the directory enumerator has been initialized.
564  mIsInitialized = PR_TRUE;
565 
566  return NS_OK;
567 }
568 
569 
574 void
576 {
577  // Indicate that the directory enumerator is no longer initialized.
578  mIsInitialized = PR_FALSE;
579 
580  // Dispose of the directory enumerator lock.
581  if (mEnumeratorLock)
582  nsAutoLock::DestroyLock(mEnumeratorLock);
583  mEnumeratorLock = nsnull;
584 
585  // Clear the directory entries enumeration stack.
586  mEntriesEnumStack.Clear();
587 
588  // Release object references.
589  mNextFile = nsnull;
590 }
591 
592 
593 //------------------------------------------------------------------------------
594 //
595 // Songbird directory enumerator private services.
596 //
597 //------------------------------------------------------------------------------
598 
606 nsresult
607 sbDirectoryEnumerator::ScanForNextFile()
608 {
609  PRBool success;
610  nsresult rv;
611 
612  // Scan for the next file.
613  while (!mNextFile && (mEntriesEnumStack.Count() > 0)) {
614  // Get the enum at the top of the stack.
615  nsCOMPtr<nsISimpleEnumerator>
616  entriesEnum = mEntriesEnumStack.ObjectAt(mEntriesEnumStack.Count() - 1);
617 
618  // If there are no more entries, pop the stack and continue.
619  PRBool hasMoreElements;
620  rv = entriesEnum->HasMoreElements(&hasMoreElements);
621  NS_ENSURE_SUCCESS(rv, rv);
622  if (!hasMoreElements) {
623  mEntriesEnumStack.RemoveObjectAt(mEntriesEnumStack.Count() - 1);
624  continue;
625  }
626 
627  // Get the next file.
628  nsCOMPtr<nsISupports> fileEntry;
629  rv = entriesEnum->GetNext(getter_AddRefs(fileEntry));
630  NS_ENSURE_SUCCESS(rv, rv);
631  nsCOMPtr<nsIFile> file = do_QueryInterface(fileEntry, &rv);
632  NS_ENSURE_SUCCESS(rv, rv);
633 
634  // Skip file if it doesn't match enumerator criteria.
635  PRBool skipFile = PR_FALSE;
636  PRBool isDirectory;
637  rv = file->IsDirectory(&isDirectory);
638  if (NS_FAILED(rv)) {
639  NS_WARNING("IsDirectory failed, assuming not a directory");
640  isDirectory = PR_FALSE;
641  }
642  if (mDirectoriesOnly) {
643  if (!isDirectory)
644  skipFile = PR_TRUE;
645  }
646  if (mFilesOnly) {
647  PRBool isFile;
648  rv = file->IsFile(&isFile);
649  if (NS_FAILED(rv)) {
650  NS_WARNING("IsFile failed, assuming not a file");
651  isFile = PR_FALSE;
652  }
653  if (!isFile)
654  skipFile = PR_TRUE;
655  }
656  if (!skipFile)
657  mNextFile = file;
658 
659  // If the current file is a directory, and the maximum depth has not been
660  // reached, enumerate the directory entries.
661  PRUint32 depth = mEntriesEnumStack.Count();
662  if (isDirectory && !((mMaxDepth > 0) && (depth >= mMaxDepth))) {
663  // Get the directory entries. If file not found is returned, the
664  // directory is empty.
665  nsRefPtr<sbDirectoryEnumeratorHelper> dirEnumeratorAdapter =
667  NS_ENSURE_TRUE(dirEnumeratorAdapter, NS_ERROR_OUT_OF_MEMORY);
668  rv = dirEnumeratorAdapter->Init(file);
669  nsCOMPtr<nsISimpleEnumerator> dirEntryEnumerator;
670  if (rv != NS_ERROR_FILE_NOT_FOUND) {
671  NS_ENSURE_SUCCESS(rv, rv);
672  dirEntryEnumerator = do_QueryInterface(dirEnumeratorAdapter, &rv);
673  NS_ENSURE_SUCCESS(rv, rv);
674  }
675  if (dirEntryEnumerator) {
676  success = mEntriesEnumStack.AppendObject(dirEntryEnumerator);
677  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
678  }
679  }
680  }
681 
682  return NS_OK;
683 }
classDescription entry
Definition: FeedWriter.js:1427
return NS_OK
_updateCookies aPath
bool IsDotDir(T const *aPath)
function succeeded(ch, cx, status, data)
NS_IMPL_THREADSAFE_ISUPPORTS1(sbDeviceCapsCompatibility, sbIDeviceCapsCompatibility) sbDeviceCapsCompatibility
boolean hasMoreElements()
Return true if more elements are available to enumerate.
function Init()
Songbird Directory Enumerator Definitions.
NS_DECL_ISUPPORTS NS_DECL_NSISIMPLEENUMERATOR nsresult Init(nsIFile *aDirectory)
var file
NS_DECL_ISUPPORTS NS_DECL_SBIDIRECTORYENUMERATOR sbDirectoryEnumerator()