]> err.no Git - sope/blob - sope-core/NGExtensions/NGDirectoryEnumerator.m
Drop apache 1 build-dependency
[sope] / sope-core / NGExtensions / NGDirectoryEnumerator.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "NGDirectoryEnumerator.h"
23 #import <Foundation/NSFileManager.h>
24 #include "common.h"
25
26 @interface NGDirectoryEnumerator(PrivateMethods)
27 - (void)recurseIntoDirectory:(NSString *)_path relativeName:(NSString *)_name;
28 - (void)backtrack;
29 - (void)findNextFile;
30 @end
31
32 @interface NGDirEntry : NSObject
33 {
34 @public
35   id           fileManager;
36   NSString     *path;
37   NSEnumerator *e;
38 }
39
40 - (id)initWithFileManager:(id<NSObject,NGFileManager>)_fm
41   path:(NSString *)_path;
42
43 - (NSString *)readdir;
44
45 @end
46
47 @implementation NGDirectoryEnumerator
48
49 - (id)initWithFileManager:(id<NSObject,NGFileManager>)_fm
50   directoryPath:(NSString *)_path 
51   recurseIntoSubdirectories:(BOOL)_recurse
52   followSymlinks:(BOOL)_follow
53   prefixFiles:(BOOL)_prefix
54 {
55   self->fileManager = _fm
56     ? [_fm retain]
57     : [[NSFileManager defaultManager] retain];
58
59   self->pathStack = [[NSMutableArray alloc] init];
60   self->enumStack = [[NSMutableArray alloc] init];
61   self->flags.isRecursive = _recurse;
62   self->flags.isFollowing = _follow;
63   
64   self->topPath = [_path copy];
65   
66   [self recurseIntoDirectory:_path relativeName:@""];
67   
68   return self;
69 }
70
71 - (id)initWithDirectoryPath:(NSString *)_path 
72   recurseIntoSubdirectories:(BOOL)_recurse
73   followSymlinks:(BOOL)_follow
74   prefixFiles:(BOOL)_prefix
75 {
76   return [self initWithFileManager:nil
77                directoryPath:_path
78                recurseIntoSubdirectories:_recurse
79                followSymlinks:_follow
80                prefixFiles:_prefix];
81 }
82
83 - (id)initWithFileManager:(id<NSObject,NGFileManager>)_fm {
84   return [self initWithFileManager:_fm
85                directoryPath:@"/"
86                recurseIntoSubdirectories:YES
87                followSymlinks:NO
88                prefixFiles:YES];
89 }
90 - (id)initWithFileManager:(id<NSObject,NGFileManager>)_fm
91   directoryPath:(NSString *)_path
92 {
93   return [self initWithFileManager:_fm
94                directoryPath:_path
95                recurseIntoSubdirectories:YES
96                followSymlinks:NO
97                prefixFiles:YES];
98 }
99
100 - (void)dealloc {
101   while ([self->pathStack count])
102     [self backtrack];
103   
104   [self->pathStack release];
105   [self->enumStack release];
106   [self->currentFileName release];
107   [self->currentFilePath release];
108   [self->topPath release];
109
110   [super dealloc];
111 }
112
113 /* accessors */
114
115 - (id<NSObject,NGFileManager>)fileManager {
116   return self->fileManager;
117 }
118
119 /* operations */
120
121 - (NSDictionary *)directoryAttributes {
122   return [self->fileManager
123               fileAttributesAtPath:self->topPath
124               traverseLink:self->flags.isFollowing];
125 }
126
127 - (NSDictionary *)fileAttributes {
128   return [self->fileManager
129               fileAttributesAtPath:self->currentFilePath
130               traverseLink:self->flags.isFollowing];
131 }
132
133 - (void)skipDescendents {
134   if ([self->pathStack count])
135     [self backtrack];
136 }
137
138 /* enumerator */
139
140 - (id)nextObject {
141   [self findNextFile];
142   return self->currentFileName;
143 }
144
145 /* internals */
146
147 - (void)recurseIntoDirectory:(NSString *)_path relativeName:(NSString *)name {
148   /* 
149      recurses into directory `path' 
150      - pushes relative path (relative to root of search) on pathStack
151      - pushes system dir enumerator on enumPath 
152   */
153   NGDirEntry *dir;
154
155   //NSLog(@"RECURSE INTO: %@", _path);
156   
157   dir = [[NGDirEntry alloc] initWithFileManager:self->fileManager path:_path];
158   
159   if (dir) {
160     [pathStack addObject:name];
161     [enumStack addObject:dir];
162   }
163 }
164
165 - (void)backtrack {
166   /*
167     backtracks enumeration to the previous dir
168     - pops current dir relative path from pathStack
169     - pops system dir enumerator from enumStack
170     - sets currentFile* to nil
171   */
172   //NSLog(@"BACKTRACK: %@", [self->pathStack lastObject]);
173   [self->enumStack removeLastObject];
174   [self->pathStack removeLastObject];
175   [self->currentFileName release]; self->currentFileName = nil;
176   [self->currentFilePath release]; self->currentFilePath = nil;
177 }
178
179 - (void)findNextFile {
180   /*
181     finds the next file according to the top enumerator
182     - if there is a next file it is put in currentFile
183     - if the current file is a directory and if isRecursive calls 
184     recurseIntoDirectory:currentFile
185     - if the current file is a symlink to a directory and if isRecursive 
186     and isFollowing calls recurseIntoDirectory:currentFile
187     - if at end of current directory pops stack and attempts to
188     find the next entry in the parent
189     - sets currentFile to nil if there are no more files to enumerate
190   */
191   NGDirEntry *dir;
192   
193   [self->currentFileName release]; self->currentFileName = nil;
194   [self->currentFilePath release]; self->currentFilePath = nil;
195     
196   while ([self->pathStack count]) {
197     NSString     *dname;
198     NSString     *dtype;
199     
200     dir = [enumStack lastObject];
201     
202     if ((dname = [dir readdir]) == nil) {
203       /* If we reached the end of this directory, go back to the upper one */
204       [self backtrack];
205       continue;
206     }
207     
208     /* Skip "." and ".." directory entries */
209     
210     if ([dname isEqualToString:@"."]) continue;
211     if ([dname isEqualToString:@".."]) continue;
212     
213     /* Name of current file */
214     
215     self->currentFileName =
216       [[[pathStack lastObject]
217                    stringByAppendingPathComponent:dname]
218                    copy];
219     
220     /* Full path of current file */
221     
222     self->currentFilePath =
223       [[self->topPath stringByAppendingPathComponent:self->currentFileName]
224                       copy];
225     
226     dtype = [[self->fileManager
227                   fileAttributesAtPath:self->currentFilePath
228                   traverseLink:self->flags.isFollowing]
229                   objectForKey:NSFileType];
230     
231     // do not follow links
232     
233     if (!flags.isFollowing) {
234       if ([dtype isEqualToString:NSFileTypeSymbolicLink])
235         /* if link then return it as link */
236         break;
237     }
238     
239     /* Follow links - check for directory */
240
241     if ([dtype isEqualToString:NSFileTypeDirectory] &&
242         self->flags.isRecursive) {
243       [self recurseIntoDirectory:self->currentFilePath 
244             relativeName:self->currentFileName];
245     }
246     
247     break;
248   }
249 }
250
251 - (NSString *)description {
252   NSMutableString *ms;
253   
254   ms = [NSMutableString stringWithCapacity:128];
255
256   [ms appendFormat:@"<%@[0x%p]: ", NSStringFromClass([self class]), self];
257
258   [ms appendFormat:@" dir='%@'", self->topPath];
259   [ms appendFormat:@" cname='%@'", self->currentFileName];
260   [ms appendFormat:@" cpath='%@'", self->currentFilePath];
261   [ms appendString:@">"];
262   
263   return ms;
264 }
265
266 @end /* NGDirectoryEnumerator */
267
268 @implementation NGDirEntry
269
270 - (id)initWithFileManager:(id<NSObject, NGFileManager>)_fm path:(NSString *)_p{
271   self->fileManager = [_fm retain];
272   self->path        = [_p copy];
273   return self;
274 }
275
276 - (void)dealloc {
277   [self->e    release];
278   [self->path release];
279   [self->fileManager release];
280   [super dealloc];
281 }
282
283 /* operations */
284
285 - (NSString *)readdir {
286   NSString *s;
287   
288   if (self->e == nil) {
289     self->e = [[[self->fileManager directoryContentsAtPath:self->path]
290                                    sortedArrayUsingSelector:
291                                      @selector(compare:)]
292                                    objectEnumerator];
293     self->e = [self->e retain];
294   }
295   
296   s = [self->e nextObject];
297   // [self logWithFormat:@"readdir: %@", s];
298   
299   return s;
300 }
301
302 @end /* NGDirEntry */