unrarsrc  6.1.7
About: unrar extracts, views and tests the contents of archives created with the RAR archiver.
  Fossies Dox: unrarsrc-6.1.7.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

scantree.cpp
Go to the documentation of this file.
1#include "rar.hpp"
2
3ScanTree::ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs)
4{
9
10 ScanEntireDisk=false;
11 FolderWildcards=false;
12
14 *CurMask=0;
15 memset(FindStack,0,sizeof(FindStack));
16 Depth=0;
17 Errors=0;
18 *ErrArcName=0;
19 Cmd=NULL;
20 ErrDirList=NULL;
22}
23
24
26{
27 for (int I=Depth;I>=0;I--)
28 if (FindStack[I]!=NULL)
29 delete FindStack[I];
30}
31
32
34{
35 if (Depth<0)
36 return SCAN_DONE;
37
38#ifndef SILENT
39 uint LoopCount=0;
40#endif
41
42 SCAN_CODE FindCode;
43 while (1)
44 {
45 if (*CurMask==0 && !GetNextMask())
46 return SCAN_DONE;
47
48#ifndef SILENT
49 // Let's return some ticks to system or WinRAR can become irresponsible
50 // while scanning files in command like "winrar a -r arc c:\file.ext".
51 // Also we reset system sleep timer here.
52 if ((++LoopCount & 0x3ff)==0)
53 Wait();
54#endif
55
56 FindCode=FindProc(FD);
57 if (FindCode==SCAN_ERROR)
58 {
59 Errors++;
60 continue;
61 }
62 if (FindCode==SCAN_NEXT)
63 continue;
64 if (FindCode==SCAN_SUCCESS && FD->IsDir && GetDirs==SCAN_SKIPDIRS)
65 continue;
66 if (FindCode==SCAN_DONE && GetNextMask())
67 continue;
68 if (FilterList.ItemsCount()>0 && FindCode==SCAN_SUCCESS)
70 continue;
71 break;
72 }
73 return FindCode;
74}
75
76
77// For masks like dir1\dir2*\*.ext in non-recursive mode.
79{
80 bool WildcardFound=false;
81 uint SlashPos=0;
82 for (int I=0;CurMask[I]!=0;I++)
83 {
84 if (CurMask[I]=='?' || CurMask[I]=='*')
85 WildcardFound=true;
86 if (WildcardFound && IsPathDiv(CurMask[I]))
87 {
88 // First path separator position after folder wildcard mask.
89 // In case of dir1\dir2*\dir3\name.ext mask it may point not to file
90 // name, so we cannot use PointToName() here.
91 SlashPos=I;
92 break;
93 }
94 }
95
96 wchar Mask[NM];
97 wcsncpyz(Mask,CurMask,ASIZE(Mask));
98 Mask[SlashPos]=0;
99
100 // Prepare the list of all folders matching the wildcard mask.
102 FindFile Find;
103 Find.SetMask(Mask);
104 FindData FD;
105 while (Find.Next(&FD))
106 if (FD.IsDir)
107 {
108 wcsncatz(FD.Name,CurMask+SlashPos,ASIZE(FD.Name));
109
110 // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched
111 // by such mask. Skipping empty dir with dir*\*.* confused some users.
112 wchar *LastMask=PointToName(FD.Name);
113 if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0)
115
117 }
119 return false;
120 // Return the first matching folder name now.
122 return true;
123}
124
125
126// For masks like dir1\dir2*\file.ext this function sets 'dir1' recursive mask
127// and '*\dir2*\file.ext' filter. Masks without folder wildcards are
128// returned as is.
130{
131 // If we have some matching folders left for non-recursive folder wildcard
132 // mask, we return it here.
134 return true;
135
136 FolderWildcards=false;
139 return false;
140
141 // Check if folder wildcards present.
142 bool WildcardFound=false;
143 uint FolderWildcardCount=0;
144 uint SlashPos=0;
145 uint StartPos=0;
146#ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard.
147 if (CurMask[0]=='\\' && CurMask[1]=='\\' && CurMask[2]=='?' && CurMask[3]=='\\')
148 StartPos=4;
149#endif
150 for (uint I=StartPos;CurMask[I]!=0;I++)
151 {
152 if (CurMask[I]=='?' || CurMask[I]=='*')
153 WildcardFound=true;
154 if (IsPathDiv(CurMask[I]) || IsDriveDiv(CurMask[I]))
155 {
156 if (WildcardFound)
157 {
158 // Calculate a number of folder wildcards in current mask.
159 FolderWildcardCount++;
160 WildcardFound=false;
161 }
162 if (FolderWildcardCount==0)
163 SlashPos=I; // Slash position before first folder wildcard mask.
164 }
165 }
166 if (FolderWildcardCount==0)
167 return true;
168 FolderWildcards=true; // Global folder wildcards flag.
169
170 // If we have only one folder wildcard component and -r is missing or -r-
171 // is specified, prepare matching folders in non-recursive mode.
172 // We assume -r for masks like dir1*\dir2*\file*, because it is complicated
173 // to fast find them using OS file find API call.
174 if ((Recurse==RECURSE_NONE || Recurse==RECURSE_DISABLE) && FolderWildcardCount==1)
175 return ExpandFolderMask();
176
177 wchar Filter[NM];
178 // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders.
179 wcsncpyz(Filter,L"*",ASIZE(Filter));
180 AddEndSlash(Filter,ASIZE(Filter));
181 // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*'
182 wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos;
183 wcsncatz(Filter,WildName,ASIZE(Filter));
184
185 // Treat dir*\* or dir*\*.* as dir\, so empty 'dir' is also matched
186 // by such mask. Skipping empty dir with dir*\*.* confused some users.
187 wchar *LastMask=PointToName(Filter);
188 if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0)
189 *LastMask=0;
190
191 FilterList.AddString(Filter);
192
193 bool RelativeDrive=IsDriveDiv(CurMask[SlashPos]);
194 if (RelativeDrive)
195 SlashPos++; // Use "d:" instead of "d" for d:* mask.
196
197 CurMask[SlashPos]=0;
198
199 if (!RelativeDrive) // Keep d: mask as is, not convert to d:\*
200 {
201 // We need to append "\*" both for -ep1 to work correctly and to
202 // convert d:\* masks previously truncated to d: back to original form.
204 wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
205 }
206 return true;
207}
208
209
211{
212 if (!GetFilteredMask())
213 return false;
214#ifdef _WIN_ALL
216#endif
217
218 // We wish to scan entire disk if mask like c:\ is specified
219 // regardless of recursion mode. Use c:\*.* mask when need to scan only
220 // the root directory.
222
224 if (*Name==0)
225 wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
226 if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0))
227 {
229 wcsncatz(CurMask,MASKALL,ASIZE(CurMask));
230 }
232 Depth=0;
233
235
236 return true;
237}
238
239
241{
242 if (*CurMask==0)
243 return SCAN_NEXT;
244 bool FastFindFile=false;
245
246 if (FindStack[Depth]==NULL) // No FindFile object for this depth yet.
247 {
248 bool Wildcards=IsWildcard(CurMask);
249
250 // If we have a file name without wildcards, we can try to use
251 // FastFind to optimize speed. For example, in Unix it results in
252 // stat call instead of opendir/readdir/closedir.
253 bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks);
254
255 // Link check is important for NTFS, where links can have "Directory"
256 // attribute, but we do not want to recurse to them in "get links" mode.
257 bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink);
258
259 // SearchAll means that we'll use "*" mask for search, so we'll find
260 // subdirectories and will be able to recurse into them.
261 // We do not use "*" for directories at any level or for files
262 // at top level in recursion mode. We always comrpess the entire directory
263 // if folder wildcard is specified.
264 bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS ||
266 Wildcards && Recurse==RECURSE_WILDCARDS ||
268 if (Depth==0)
269 SearchAllInRoot=SearchAll;
270 if (SearchAll || Wildcards)
271 {
272 // Create the new FindFile object for wildcard based search.
274
275 wchar SearchMask[NM];
276 wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask));
277 if (SearchAll)
278 SetName(SearchMask,MASKALL,ASIZE(SearchMask));
279 FindStack[Depth]->SetMask(SearchMask);
280 }
281 else
282 {
283 // Either we failed to fast find or we found a file or we found
284 // a directory in RECURSE_DISABLE mode, so we do not need to scan it.
285 // We can return here and do not need to process further.
286 // We need to process further only if we fast found a directory.
287 if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE)
288 {
289 // Return SCAN_SUCCESS if we found a file.
290 SCAN_CODE RetCode=SCAN_SUCCESS;
291
292 if (!FindCode)
293 {
294 // Return SCAN_ERROR if problem is more serious than just
295 // "file not found".
296 RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT;
297
298 // If we failed to find an object, but our current mask is excluded,
299 // we skip this object and avoid indicating an error.
300 if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true))
301 RetCode=SCAN_NEXT;
302 else
303 {
305 // User asked to return RARX_NOFILES and not RARX_OPEN here.
307 }
308 }
309
310 // If we searched only for one file or directory in "fast find"
311 // (without a wildcard) mode, let's set masks to zero,
312 // so calling function will know that current mask is used
313 // and next one must be read from mask list for next call.
314 // It is not necessary for directories, because even in "fast find"
315 // mode, directory recursing will quit by (Depth < 0) condition,
316 // which returns SCAN_DONE to calling function.
317 *CurMask=0;
318
319 return RetCode;
320 }
321
322 // We found a directory using only FindFile::FastFind function.
323 FastFindFile=true;
324 }
325 }
326
327 if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks))
328 {
329 // We cannot find anything more in directory either because of
330 // some error or just as result of all directory entries already read.
331
332 bool Error=FD->Error;
333 if (Error)
334 ScanError(Error);
335
336 wchar DirName[NM];
337 *DirName=0;
338
339 // Going to at least one directory level higher.
340 delete FindStack[Depth];
341 FindStack[Depth--]=NULL;
342 while (Depth>=0 && FindStack[Depth]==NULL)
343 Depth--;
344 if (Depth < 0)
345 {
346 // Directories scanned both in normal and FastFindFile mode,
347 // finally exit from scan here, by (Depth < 0) condition.
348
349 if (Error)
350 Errors++;
351 return SCAN_DONE;
352 }
353
354 wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER);
355 if (Slash!=NULL)
356 {
357 wchar Mask[NM];
358 wcsncpyz(Mask,Slash,ASIZE(Mask));
360 wcsncpyz(Mask+1,PointToName(OrigCurMask),ASIZE(Mask)-1);
361 *Slash=0;
362 wcsncpyz(DirName,CurMask,ASIZE(DirName));
363 wchar *PrevSlash=wcsrchr(CurMask,CPATHDIVIDER);
364 if (PrevSlash==NULL)
365 wcsncpyz(CurMask,Mask+1,ASIZE(CurMask));
366 else
367 {
368 *PrevSlash=0;
370 }
371 }
373 FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir)
374 {
376 return Error ? SCAN_ERROR:SCAN_SUCCESS;
377 }
378 return Error ? SCAN_ERROR:SCAN_NEXT;
379 }
380
381 // Link check is required for NTFS links, not for Unix.
382 if (FD->IsDir && (!GetLinks || !FD->IsLink))
383 {
384 // If we found the directory in top (Depth==0) directory
385 // and if we are not in "fast find" (directory name only as argument)
386 // or in recurse (SearchAll was set when opening the top directory) mode,
387 // we do not recurse into this directory. We either return it by itself
388 // or skip it.
389 if (!FastFindFile && Depth==0 && !SearchAllInRoot)
391
392 // Let's check if directory name is excluded, so we do not waste
393 // time searching in directory, which will be excluded anyway.
394 if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) ||
396 {
397 // If we are here in "fast find" mode, it means that entire directory
398 // specified in command line is excluded. Then we need to return
399 // SCAN_DONE to go to next mask and avoid the infinite loop
400 // in GetNext() function. Such loop would be possible in case of
401 // SCAN_NEXT code and "rar a arc dir -xdir" command.
402
403 return FastFindFile ? SCAN_DONE:SCAN_NEXT;
404 }
405
406 wchar Mask[NM];
407
408 wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask));
410
411 if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1)
412 {
413 uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask);
414 return SCAN_ERROR;
415 }
416
419
420 Depth++;
421
422 // We need to use OrigCurMask for depths less than SetAllMaskDepth
423 // and "*" for depths equal or larger than SetAllMaskDepth.
424 // It is important when "fast finding" directories at Depth > 0.
425 // For example, if current directory is RootFolder and we compress
426 // the following directories structure:
427 // RootFolder
428 // +--Folder1
429 // | +--Folder2
430 // | +--Folder3
431 // +--Folder4
432 // with 'rar a -r arcname Folder2' command, rar could add not only
433 // Folder1\Folder2 contents, but also Folder1\Folder3 if we were using
434 // "*" mask at all levels. We need to use "*" mask inside of Folder2,
435 // but return to "Folder2" mask when completing scanning Folder2.
436 // We can rewrite SearchAll expression above to avoid fast finding
437 // directories at Depth > 0, but then 'rar a -r arcname Folder2'
438 // will add the empty Folder2 and do not add its contents.
439
440 if (FastFindFile)
442 }
443 if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES))
444 return SCAN_NEXT;
445
446 return SCAN_SUCCESS;
447}
448
449
450void ScanTree::ScanError(bool &Error)
451{
452#ifdef _WIN_ALL
453 if (Error)
454 {
455 // Get attributes of parent folder and do not display an error
456 // if it is reparse point. We cannot scan contents of standard
457 // Windows reparse points like "C:\Documents and Settings"
458 // and we do not want to issue numerous useless errors for them.
459 // We cannot just check FD->FileAttr here, it can be undefined
460 // if we process "folder\*" mask or if we process "folder" mask,
461 // but "folder" is inaccessible.
462 wchar *Slash=PointToName(CurMask);
463 if (Slash>CurMask)
464 {
465 *(Slash-1)=0;
466 DWORD Attr=GetFileAttributes(CurMask);
467 *(Slash-1)=CPATHDIVIDER;
468 if (Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
469 Error=false;
470 }
471
472 // Do not display an error if we cannot scan contents of
473 // "System Volume Information" folder. Normally it is not accessible.
474 if (wcsstr(CurMask,L"System Volume Information\\")!=NULL)
475 Error=false;
476 }
477#endif
478
479 if (Error && Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true))
480 Error=false;
481
482 if (Error)
483 {
484 if (ErrDirList!=NULL)
486 if (ErrDirSpecPathLength!=NULL)
488 wchar FullName[NM];
489 // This conversion works for wildcard masks too.
490 ConvertNameToFull(CurMask,FullName,ASIZE(FullName));
491 uiMsg(UIERROR_DIRSCAN,FullName);
493 }
494}
ErrorHandler ErrHandler
void Push(T Item)
Definition: array.hpp:177
bool ExclCheck(const wchar *CheckName, bool Dir, bool CheckFullPath, bool CheckInclList)
Definition: cmdfilter.cpp:4
static bool CheckArgs(StringList *Args, bool Dir, const wchar *CheckName, bool CheckFullPath, int MatchMode)
Definition: cmdfilter.cpp:16
bool ExclDirByAttr(uint FileAttr)
Definition: cmdfilter.cpp:112
void OpenErrorMsg(const wchar *FileName)
Definition: errhnd.cpp:164
void SetErrorCode(RAR_EXIT Code)
Definition: errhnd.cpp:243
void SysErrMsg()
Definition: errhnd.cpp:368
void SetMask(const wchar *Mask)
Definition: find.cpp:27
bool Next(FindData *fd, bool GetSymLink=false)
Definition: find.cpp:34
static bool FastFind(const wchar *FindMask, FindData *fd, bool GetSymLink=false)
Definition: find.cpp:108
bool SearchAllInRoot
Definition: scantree.hpp:57
~ScanTree()
Definition: scantree.cpp:25
wchar ErrArcName[NM]
Definition: scantree.hpp:60
bool FolderWildcards
Definition: scantree.hpp:55
int Depth
Definition: scantree.hpp:28
StringList * ErrDirList
Definition: scantree.hpp:51
FindFile * FindStack[(NM/2)]
Definition: scantree.hpp:27
bool GetNextMask()
Definition: scantree.cpp:210
bool ExpandFolderMask()
Definition: scantree.cpp:78
SCAN_CODE GetNext(FindData *FindData)
Definition: scantree.cpp:33
void ScanError(bool &Error)
Definition: scantree.cpp:450
RECURSE_MODE Recurse
Definition: scantree.hpp:33
SCAN_CODE FindProc(FindData *FD)
Definition: scantree.cpp:240
bool ScanEntireDisk
Definition: scantree.hpp:39
int SetAllMaskDepth
Definition: scantree.hpp:30
CommandData * Cmd
Definition: scantree.hpp:62
bool GetLinks
Definition: scantree.hpp:34
StringList * FileMasks
Definition: scantree.hpp:32
ScanTree(StringList *FileMasks, RECURSE_MODE Recurse, bool GetLinks, SCAN_DIRS GetDirs)
Definition: scantree.cpp:3
wchar OrigCurMask[NM]
Definition: scantree.hpp:42
StringList ExpandedFolderList
Definition: scantree.hpp:45
wchar CurMask[NM]
Definition: scantree.hpp:41
size_t SpecPathLength
Definition: scantree.hpp:58
SCAN_DIRS GetDirs
Definition: scantree.hpp:35
int Errors
Definition: scantree.hpp:36
Array< uint > * ErrDirSpecPathLength
Definition: scantree.hpp:52
StringList FilterList
Definition: scantree.hpp:48
bool GetFilteredMask()
Definition: scantree.cpp:129
bool GetString(wchar *Str, size_t MaxLength)
Definition: strlist.cpp:49
void AddString(const wchar *Str)
Definition: strlist.cpp:26
size_t ItemsCount()
Definition: strlist.hpp:24
void Reset()
Definition: strlist.cpp:9
@ RARX_NOFILES
Definition: errhnd.hpp:16
bool IsDir(uint Attr)
Definition: filefn.cpp:218
@ FDDF_SECONDDIR
Definition: find.hpp:5
bool CmpName(const wchar *Wildcard, const wchar *Name, int CmpMode)
Definition: match.cpp:19
@ MATCH_WILDSUBPATH
Definition: match.hpp:29
@ MATCH_NAMES
Definition: match.hpp:5
RECURSE_MODE
Definition: options.hpp:32
@ RECURSE_NONE
Definition: options.hpp:33
@ RECURSE_WILDCARDS
Definition: options.hpp:36
@ RECURSE_DISABLE
Definition: options.hpp:34
@ RECURSE_ALWAYS
Definition: options.hpp:35
bool IsWildcard(const wchar *Str)
Definition: pathfn.cpp:121
wchar * PointToName(const wchar *Path)
Definition: pathfn.cpp:3
bool IsPathDiv(int Ch)
Definition: pathfn.cpp:134
void RemoveNameFromPath(wchar *Path)
Definition: pathfn.cpp:208
void UnixSlashToDos(const char *SrcName, char *DestName, size_t MaxLength)
Definition: pathfn.cpp:491
void SetName(wchar *FullName, const wchar *Name, size_t MaxSize)
Definition: pathfn.cpp:67
void ConvertNameToFull(const wchar *Src, wchar *Dest, size_t MaxSize)
Definition: pathfn.cpp:527
void AddEndSlash(wchar *Path, size_t MaxLength)
Definition: pathfn.cpp:170
bool IsDriveDiv(int Ch)
Definition: pathfn.cpp:144
bool IsDriveLetter(const wchar *Path)
Definition: pathfn.cpp:154
#define ASIZE(x)
Definition: rardefs.hpp:10
wchar_t wchar
Definition: rartypes.hpp:13
unsigned int uint
Definition: rartypes.hpp:8
#define MAXSCANDEPTH
Definition: scantree.hpp:14
SCAN_DIRS
Definition: scantree.hpp:5
@ SCAN_GETCURDIRS
Definition: scantree.hpp:9
@ SCAN_GETDIRSTWICE
Definition: scantree.hpp:8
@ SCAN_SKIPDIRS
Definition: scantree.hpp:6
SCAN_CODE
Definition: scantree.hpp:12
@ SCAN_ERROR
Definition: scantree.hpp:12
@ SCAN_NEXT
Definition: scantree.hpp:12
@ SCAN_SUCCESS
Definition: scantree.hpp:12
@ SCAN_DONE
Definition: scantree.hpp:12
void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen)
Definition: strfn.cpp:275
void wcsncatz(wchar *dest, const wchar *src, size_t maxlen)
Definition: strfn.cpp:300
Definition: find.hpp:9
wchar Name[NM]
Definition: find.hpp:10
uint Flags
Definition: find.hpp:23
uint FileAttr
Definition: find.hpp:12
bool IsLink
Definition: find.hpp:14
bool Error
Definition: find.hpp:24
bool IsDir
Definition: find.hpp:13
void Wait()
Definition: system.cpp:80
void uiMsg(UIMESSAGE_CODE Code)
Definition: ui.hpp:148
@ UIERROR_DIRSCAN
Definition: ui.hpp:38
@ UIERROR_PATHTOOLONG
Definition: ui.hpp:38