Back to index

lightning-sunbird  0.9+nobinonly
inFileSearch.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is mozilla.org code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2001
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Joe Hewitt <hewitt@netscape.com> (original author)
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "inFileSearch.h"
00039 
00040 #include "nsCOMPtr.h"
00041 #include "nsString.h"
00042 #include "nsReadableUtils.h"
00043 
00045 
00046 inFileSearch::inFileSearch()
00047   : mSearchLoop(nsnull),
00048     mBasePath(nsnull),
00049     mTextCriteria(nsnull),
00050     mFilenameCriteria(nsnull),
00051     mDirsSearched(0),
00052     mFilenameCriteriaCount(0),
00053     mResultCount(0),
00054     mIsActive(PR_FALSE),
00055     mHoldResults(PR_FALSE),
00056     mReturnRelativePaths(PR_FALSE),
00057     mSearchRecursive(PR_FALSE)
00058 {
00059 }
00060 
00061 inFileSearch::~inFileSearch()
00062 {
00063   delete mSearchLoop;
00064   delete mTextCriteria;
00065 }
00066 
00067 NS_IMPL_ISUPPORTS2(inFileSearch, inISearchProcess, inIFileSearch)
00068 
00069 
00070 // inISearchProcess
00071 
00072 NS_IMETHODIMP 
00073 inFileSearch::GetIsActive(PRBool *aIsActive)
00074 {
00075   *aIsActive = mIsActive;
00076   return NS_OK;
00077 }
00078 
00079 NS_IMETHODIMP 
00080 inFileSearch::GetResultCount(PRInt32 *aResultCount)
00081 {
00082   *aResultCount = mResultCount;
00083   return NS_OK;
00084 }
00085 
00086 NS_IMETHODIMP 
00087 inFileSearch::GetHoldResults(PRBool *aHoldResults)
00088 {
00089   *aHoldResults = mHoldResults;
00090   return NS_OK;
00091 }
00092 
00093 NS_IMETHODIMP 
00094 inFileSearch::SetHoldResults(PRBool aHoldResults)
00095 {
00096   mHoldResults = aHoldResults;
00097   return NS_OK;
00098 }
00099 
00100 NS_IMETHODIMP 
00101 inFileSearch::SearchSync()
00102 {
00103 /*  if (mSearchPath)
00104     SearchDirectory(mSearchPath, PR_TRUE);
00105   else {
00106     return NS_ERROR_FAILURE;
00107   }
00108 */
00109   return NS_ERROR_NOT_IMPLEMENTED;
00110 }
00111 
00112 NS_IMETHODIMP
00113 inFileSearch::SearchAsync(inISearchObserver *aObserver)
00114 {
00115   mObserver = aObserver;
00116   mObserver->OnSearchStart(this);
00117   
00118   InitSearch();
00119   InitSubDirectoryStack();
00120   InitSearchLoop();
00121   
00122   if (mSearchPath) {
00123     // start off by searching the first directory
00124     SearchDirectory(mSearchPath, PR_FALSE);
00125     
00126     if (mSearchRecursive) {
00127       // start the loop to continue searching
00128       mIsActive = PR_TRUE;
00129       mSearchLoop->Start();
00130     } else {
00131       KillSearch(inISearchObserver::SUCCESS);
00132     }
00133   } else {
00134     mObserver->OnSearchError(this, NS_LITERAL_STRING("No search path has been provided"));
00135     KillSearch(inISearchObserver::ERROR);
00136   }
00137 
00138   return NS_OK;
00139 }
00140 
00141 NS_IMETHODIMP
00142 inFileSearch::SearchStop()
00143 {
00144   KillSearch(inISearchObserver::INTERRUPTED);
00145   return NS_OK;
00146 }
00147 
00148 NS_IMETHODIMP
00149 inFileSearch::SearchStep(PRBool* _retval)
00150 {
00151   nsCOMPtr<nsIFile> nextDir;
00152   PRBool more = GetNextSubDirectory(getter_AddRefs(nextDir));
00153 
00154   if (more) {
00155     SearchDirectory(nextDir, PR_FALSE);
00156   } else {
00157     KillSearch(inISearchObserver::SUCCESS);
00158     *_retval = PR_TRUE;
00159   }
00160 
00161   return NS_OK;
00162 }
00163 
00164 NS_IMETHODIMP 
00165 inFileSearch::GetStringResultAt(PRInt32 aIndex, nsAString& _retval)
00166 {
00167   nsCOMPtr<nsIFile> file;
00168 
00169   _retval.Truncate();
00170 
00171   if (mHoldResults) {
00172     if (aIndex < mResults.Count()) {
00173       file = mResults[aIndex];
00174     }
00175   } else if (aIndex == mResultCount-1 && mLastResult) {
00176     // get the path of the last result as an nsAutoString
00177     file = mLastResult;
00178   } 
00179   
00180   if (file) {
00181     mLastResult->GetPath(_retval);
00182     if (mReturnRelativePaths)
00183       MakePathRelative(_retval);
00184   } else {
00185     return NS_ERROR_FAILURE;
00186   }
00187 
00188   return NS_OK;
00189 }
00190 
00191 NS_IMETHODIMP 
00192 inFileSearch::GetIntResultAt(PRInt32 aIndex, PRInt32 *_retval)
00193 {
00194   return NS_ERROR_NOT_IMPLEMENTED;
00195 }
00196 
00197 NS_IMETHODIMP 
00198 inFileSearch::GetUIntResultAt(PRInt32 aIndex, PRUint32 *_retval)
00199 {
00200   return NS_ERROR_NOT_IMPLEMENTED;
00201 }
00202 
00204 // inIFileSearch
00205 
00206 NS_IMETHODIMP 
00207 inFileSearch::GetBasePath(PRUnichar** aBasePath)
00208 {
00209   if (mBasePath) {
00210     *aBasePath = ToNewUnicode(*mBasePath);
00211   } else {
00212     return NS_ERROR_FAILURE;
00213   }
00214   return NS_OK;
00215 }
00216 
00217 NS_IMETHODIMP 
00218 inFileSearch::SetBasePath(const PRUnichar* aBasePath)
00219 {
00220   mBasePath = new nsAutoString();
00221   mBasePath->Assign(aBasePath);
00222   return NS_OK;
00223 }
00224 
00225 NS_IMETHODIMP 
00226 inFileSearch::GetReturnRelativePaths(PRBool* aReturnRelativePaths)
00227 {
00228   *aReturnRelativePaths = mReturnRelativePaths;
00229   return NS_OK;
00230 }
00231 
00232 NS_IMETHODIMP 
00233 inFileSearch::SetReturnRelativePaths(PRBool aReturnRelativePaths)
00234 {
00235   mReturnRelativePaths = aReturnRelativePaths;
00236   return NS_OK;
00237 }
00238 
00239 NS_IMETHODIMP 
00240 inFileSearch::GetFilenameCriteria(PRUnichar** aFilenameCriteria)
00241 {
00242   // TODO: reconstruct parsed filename criteria into string
00243   return NS_OK;
00244 }
00245 
00246 NS_IMETHODIMP 
00247 inFileSearch::SetFilenameCriteria(const PRUnichar* aFilenameCriteria)
00248 {
00249   // first pass: scan for commas so we know how long to make array
00250   PRUint32 idx = 0;
00251   PRUint32 commas = 0;
00252   const PRUnichar* c = aFilenameCriteria;
00253   while (*c) {
00254     if (*c == ',')
00255         ++commas;
00256     ++c;
00257   }
00258   
00259   mFilenameCriteria = new PRUnichar*[commas+1];
00260   mFilenameCriteriaCount = 0;
00261 
00262   // second pass: split up at commas and insert into array
00263   idx = 0;
00264   PRInt32 lastComma = -1;
00265   PRUnichar* buf = new PRUnichar[257];
00266   c = aFilenameCriteria;
00267   PRBool going = PR_TRUE;
00268   while (going) {
00269     if (*c == ',' || !*c) {
00270       buf[idx-lastComma-1] = 0;
00271       lastComma = idx;
00272       mFilenameCriteria[mFilenameCriteriaCount] = buf;
00273       ++mFilenameCriteriaCount;
00274       if (*c)
00275         buf = new PRUnichar[257];
00276       else
00277         going = PR_FALSE;
00278     } else {
00279       buf[idx-lastComma-1] = *c;
00280     }
00281     ++c;
00282     ++idx;
00283   }
00284 
00285   return NS_OK;
00286 }
00287 
00288 NS_IMETHODIMP 
00289 inFileSearch::GetTextCriteria(PRUnichar** aTextCriteria)
00290 {
00291   *aTextCriteria = ToNewUnicode(*mTextCriteria);
00292   return NS_OK;
00293 }
00294 
00295 NS_IMETHODIMP 
00296 inFileSearch::SetTextCriteria(const PRUnichar* aTextCriteria)
00297 {
00298   mTextCriteria = new nsAutoString();
00299   mTextCriteria->Assign(aTextCriteria);
00300   return NS_OK;
00301 }
00302 
00303 NS_IMETHODIMP 
00304 inFileSearch::GetSearchPath(nsIFile** aSearchPath)
00305 {
00306   *aSearchPath = mSearchPath;
00307   NS_IF_ADDREF(*aSearchPath);
00308   return NS_OK;
00309 }
00310 
00311 NS_IMETHODIMP 
00312 inFileSearch::SetSearchPath(nsIFile* aSearchPath)
00313 {
00314   mSearchPath = aSearchPath;
00315   return NS_OK;
00316 }
00317 
00318 NS_IMETHODIMP 
00319 inFileSearch::GetSearchRecursive(PRBool* aSearchRecursive)
00320 {
00321   *aSearchRecursive = mSearchRecursive;
00322   return NS_OK;
00323 }
00324 
00325 NS_IMETHODIMP 
00326 inFileSearch::SetSearchRecursive(PRBool aSearchRecursive)
00327 {
00328   mSearchRecursive = aSearchRecursive;
00329   return NS_OK;
00330 }
00331 
00332 NS_IMETHODIMP 
00333 inFileSearch::GetDirectoriesSearched(PRUint32* aDirectoriesSearched)
00334 {
00335   *aDirectoriesSearched = mDirsSearched;
00336   return NS_OK;
00337 }
00338 
00339 NS_IMETHODIMP 
00340 inFileSearch::GetCurrentDirectory(nsIFile** aCurrentDirectory)
00341 {
00342   return NS_ERROR_NOT_IMPLEMENTED;
00343 }
00344 
00345 NS_IMETHODIMP 
00346 inFileSearch::GetFileResultAt(PRInt32 aIndex, nsIFile** _retval)
00347 {
00348   if (mHoldResults) {
00349     if (aIndex < mResults.Count()) {
00350       NS_IF_ADDREF(*_retval = mResults[aIndex]);
00351     }
00352   } else if (aIndex == mResultCount-1 && mLastResult) {
00353     *_retval = mLastResult;
00354     NS_IF_ADDREF(*_retval);
00355   } else {
00356     return NS_ERROR_FAILURE;
00357   }
00358   return NS_OK;
00359 }
00360 
00361 NS_IMETHODIMP 
00362 inFileSearch::GetDirectoryDepth(nsIFile* aDir, PRUint32* _retval)
00363 {
00364   *_retval = 0;
00365   return CountDirectoryDepth(aDir, _retval);
00366 }
00367 
00368 NS_IMETHODIMP 
00369 inFileSearch::GetSubDirectories(nsIFile* aDir, nsISupportsArray** _retval)
00370 {
00371   return NS_ERROR_NOT_IMPLEMENTED;
00372 }
00373 
00374 
00376 // inFileSearch
00377 
00378 nsresult
00379 inFileSearch::InitSearch()
00380 {
00381   mResults.Clear();
00382   
00383   mLastResult = nsnull;
00384   mResultCount = 0;
00385   mDirsSearched = 0;
00386   return NS_OK;
00387 }
00388 
00389 nsresult
00390 inFileSearch::KillSearch(PRInt16 aResult)
00391 {
00392   mIsActive = PR_TRUE;
00393   mObserver->OnSearchEnd(this, aResult);
00394 
00395   return NS_OK;
00396 }
00397 
00398 nsresult 
00399 inFileSearch::SearchDirectory(nsIFile* aDir, PRBool aIsSync)
00400 {
00401   ++mDirsSearched;
00402 
00403   // recurse through subdirectories
00404   nsISimpleEnumerator* entries;
00405   aDir->GetDirectoryEntries(&entries);
00406 
00407   if (!aIsSync) {
00408     // store this directory for next step in async search
00409     PushSubDirectoryOnStack(aDir);
00410   }
00411   
00412   PRBool hasMoreElements;
00413   PRBool isDirectory;
00414   nsCOMPtr<nsIFile> entry;
00415 
00416   entries->HasMoreElements(&hasMoreElements);
00417   while (hasMoreElements) {
00418     entries->GetNext(getter_AddRefs(entry));
00419     entries->HasMoreElements(&hasMoreElements);
00420 
00421     entry->IsDirectory(&isDirectory);
00422     if (isDirectory && aIsSync) {
00423       // this is a directory, so search it now (only if synchronous)
00424       if (aIsSync) 
00425         SearchDirectory(entry, aIsSync);
00426     } else {
00427       // this is a file, so see if it matches
00428       if (MatchFile(entry)) {
00429         PrepareResult(entry, aIsSync);
00430       }
00431     }
00432   }
00433 
00434   return NS_OK;
00435 }
00436 
00437 nsresult
00438 inFileSearch::PrepareResult(nsIFile* aFile, PRBool aIsSync)
00439 {
00440   if (aIsSync || mHoldResults) {
00441     mResults.AppendObject(aFile);
00442   }
00443 
00444   if (!aIsSync) {
00445     ++mResultCount;
00446     mLastResult = aFile;
00447     mObserver->OnSearchResult(this);
00448   } 
00449 
00450   return NS_OK;
00451 }
00452 
00453 nsresult
00454 inFileSearch::InitSearchLoop()
00455 {
00456   if (!mSearchLoop) {
00457     nsCOMPtr<inISearchProcess> process = do_QueryInterface(this);
00458     mSearchLoop = new inSearchLoop(process);
00459   }
00460   return NS_OK;
00461 }
00462 
00464 // Subdirectory stack (for asynchronous searches)
00465 
00466 nsresult 
00467 inFileSearch::InitSubDirectoryStack()
00468 {
00469   mDirStack.Clear();
00470 
00471   return NS_OK;
00472 }
00473 
00474 PRBool
00475 inFileSearch::GetNextSubDirectory(nsIFile** aDir)
00476 {
00477   // get the enumerator on top of the stack
00478   nsCOMPtr<nsISimpleEnumerator> nextDirs;
00479   while (PR_TRUE) {
00480     PRInt32 count = mDirStack.Count();
00481     // the stack is empty, so our search must be complete
00482     if (count == 0) return PR_FALSE;
00483 
00484     // get the next directory enumerator on the stack
00485     nextDirs = mDirStack[count-1];
00486 
00487     // get the next directory from the enumerator
00488     *aDir = GetNextDirectory(nextDirs).get();
00489   
00490     if (*aDir)  {
00491       // this enumerator is ready to rock, so let's move on
00492       return PR_TRUE;
00493     }
00494 
00495     // enumerator is done, so pop it off the stack
00496     mDirStack.RemoveObjectAt(count-1);
00497   } 
00498 
00499   
00500   return PR_TRUE;
00501 }
00502 
00503 nsresult 
00504 inFileSearch::PushSubDirectoryOnStack(nsIFile* aDir)
00505 {
00506   nsCOMPtr<nsISimpleEnumerator> entries;
00507   aDir->GetDirectoryEntries(getter_AddRefs(entries));
00508   mDirStack.AppendObject(entries);
00509   return NS_OK;
00510 }
00511 
00512 already_AddRefed<nsIFile>
00513 inFileSearch::GetNextDirectory(nsISimpleEnumerator* aEnum)
00514 {
00515   nsCOMPtr<nsIFile> file;
00516   nsCOMPtr<nsISupports> supports;
00517   PRBool isDir;
00518   PRBool hasMoreElements;
00519 
00520   while (PR_TRUE) {
00521     aEnum->HasMoreElements(&hasMoreElements);
00522     if (!hasMoreElements) 
00523       break;
00524     aEnum->GetNext(getter_AddRefs(supports));
00525     file = do_QueryInterface(supports);
00526     file->IsDirectory(&isDir);
00527     if (isDir)
00528       break;
00529   } 
00530 
00531   nsIFile* f = file.get();
00532   NS_IF_ADDREF(f);
00533 
00534   return isDir ? f : nsnull;
00535 }
00536 
00538 // Pattern Matching
00539 
00540 PRBool
00541 inFileSearch::MatchFile(nsIFile* aFile)
00542 {
00543   nsAutoString fileName;
00544   aFile->GetLeafName(fileName);
00545 
00546   PRUnichar* fileNameUnicode = ToNewUnicode(fileName);
00547   
00548   PRBool match;
00549 
00550   for (PRUint32 i = 0; i < mFilenameCriteriaCount; ++i) {
00551     match = MatchPattern(mFilenameCriteria[i], fileNameUnicode);
00552     if (match) return PR_TRUE;
00553   }
00554 
00555   // XXX are we leaking fileNameUnicode?
00556   return PR_FALSE;
00557 }
00558 
00559 PRBool
00560 inFileSearch::MatchPattern(PRUnichar* aPattern, PRUnichar* aString)
00561 {
00562   PRInt32 index = 0;
00563   PRBool matching = PR_TRUE;
00564   char wildcard = '*';
00565   
00566   PRUnichar* patternPtr = aPattern;
00567   PRUnichar* stringPtr = aString;
00568 
00569   while (matching && *patternPtr && *stringPtr) {
00570     if (*patternPtr == wildcard) {
00571       matching = AdvanceWildcard(&stringPtr, patternPtr+1);
00572     } else {
00573       matching = *patternPtr == *stringPtr;
00574       ++stringPtr;
00575     }
00576     if (!matching) return PR_FALSE;
00577     ++patternPtr;
00578     ++index;
00579   }
00580 
00581   return matching;
00582 }
00583 
00584 PRBool
00585 inFileSearch::AdvanceWildcard(PRUnichar** aString, PRUnichar* aNextChar)
00586 {
00587   PRUnichar* stringPtr = *aString;
00588 
00589   while (1) {
00590     if (*stringPtr == *aNextChar) {
00591       // we have found the next char after the wildcard, so return with success
00592       *aString = stringPtr;
00593       return PR_TRUE;
00594     } else if (*stringPtr == 0)
00595       return PR_FALSE;
00596     ++stringPtr;
00597   }
00598 }
00599 
00601 // URL fixing
00602 
00603 nsresult
00604 inFileSearch::MakePathRelative(nsAString& aPath)
00605 {
00606 
00607   // get an nsAutoString version of the search path
00608   nsAutoString searchPath;
00609   mSearchPath->GetPath(searchPath);
00610 
00611   nsAutoString result;
00612   PRUint32 len = searchPath.Length();
00613   if (Substring(aPath, 0, len) == searchPath) {
00614     result = Substring(aPath, len+1, aPath.Length() - len - 1);
00615     result.ReplaceChar('\\', '/');
00616   }
00617   aPath = result;
00618 
00619   return NS_OK;
00620 }
00621 
00622 nsresult
00623 inFileSearch::CountDirectoryDepth(nsIFile* aDir, PRUint32* aDepth)
00624 {
00625   ++(*aDepth);
00626 
00627   nsISimpleEnumerator* entries;
00628   aDir->GetDirectoryEntries(&entries);
00629 
00630   PRBool hasMoreElements;
00631   PRBool isDirectory;
00632   nsCOMPtr<nsIFile> entry;
00633 
00634   entries->HasMoreElements(&hasMoreElements);
00635   while (hasMoreElements) {
00636     entries->GetNext(getter_AddRefs(entry));
00637     entries->HasMoreElements(&hasMoreElements);
00638 
00639     entry->IsDirectory(&isDirectory);
00640     if (isDirectory) {
00641       CountDirectoryDepth(entry, aDepth);
00642     }
00643   }
00644 
00645   return NS_OK;
00646 }