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