|
1 " |
|
2 COPYRIGHT (c) 1989-93 by Claus Gittinger |
|
3 All Rights Reserved |
|
4 |
|
5 This software is furnished under a license and may be used |
|
6 only in accordance with the terms of that license and with the |
|
7 inclusion of the above copyright notice. This software may not |
|
8 be provided or otherwise made available to, or used by, any |
|
9 other person. No title to or ownership of the software is |
|
10 hereby transferred. |
|
11 " |
|
12 |
|
13 Collection subclass:#FileDirectory |
|
14 instanceVariableNames:'pathName lazy' |
|
15 classVariableNames:'pathOfCurrentDirectory' |
|
16 poolDictionaries:'' |
|
17 category:'Collections-Files' |
|
18 ! |
|
19 |
|
20 FileDirectory comment:' |
|
21 |
|
22 COPYRIGHT (c) 1989-93 by Claus Gittinger |
|
23 All Rights Reserved |
|
24 |
|
25 FileDirectories represent directories in the underlying host system. |
|
26 They provide various methods to create/delete and query for files and/or |
|
27 directories. |
|
28 |
|
29 %W% %E% |
|
30 |
|
31 written winter 89 by claus |
|
32 '! |
|
33 |
|
34 !FileDirectory class methodsFor:'instance creation'! |
|
35 |
|
36 rootDirectory |
|
37 "create and return a new FileDirectory for the root directory" |
|
38 |
|
39 ^ (self basicNew) pathName:'/' |
|
40 ! |
|
41 |
|
42 currentDirectory |
|
43 "create and return a new FileDirectory for the current directory" |
|
44 |
|
45 ^ (self basicNew) pathName:'.' |
|
46 ! |
|
47 |
|
48 directoryNamed:name |
|
49 "create and return a new FileDirectory for the directory |
|
50 with given pathname" |
|
51 |
|
52 ^ (self basicNew) pathName:name |
|
53 ! |
|
54 |
|
55 directoryNamed:name in:aFileDirectory |
|
56 "create and return a new FileDirectory for the directory with given name |
|
57 in another FileDirectory" |
|
58 |
|
59 |baseName| |
|
60 |
|
61 ((name at:1) == $/) ifTrue:[ |
|
62 ^ self directoryNamed:name |
|
63 ]. |
|
64 (aFileDirectory isKindOf:FileDirectory) ifTrue:[ |
|
65 baseName := aFileDirectory pathName |
|
66 ] ifFalse:[ |
|
67 baseName := aFileDirectory |
|
68 ]. |
|
69 " |
|
70 (name = '..') ifTrue:[ |
|
71 ^ (self basicNew) pathName:(OperatingSystem directoryNameOf:baseName) |
|
72 ]. |
|
73 " |
|
74 (name = '.') ifTrue:[^ aFileDirectory]. |
|
75 |
|
76 (baseName = '/') ifFalse:[ |
|
77 (baseName endsWith:'/') ifFalse:[ |
|
78 baseName := baseName , '/' |
|
79 ] |
|
80 ]. |
|
81 ^ (self basicNew) pathName:(baseName , name) |
|
82 ! ! |
|
83 |
|
84 !FileDirectory methodsFor:'accessing'! |
|
85 |
|
86 baseName |
|
87 "return my baseName |
|
88 - thats the directory name without leading parent-dirs" |
|
89 |
|
90 lazy ifTrue:[self getFullPathName]. |
|
91 ^ OperatingSystem baseNameOf:pathName |
|
92 ! |
|
93 |
|
94 directoryName |
|
95 "return my directoryName |
|
96 - thats the directory name where I'm in" |
|
97 |
|
98 lazy ifTrue:[self getFullPathName]. |
|
99 ^ OperatingSystem directoryNameOf:pathName |
|
100 ! |
|
101 |
|
102 pathName |
|
103 "return my full pathname" |
|
104 |
|
105 lazy ifTrue:[self getFullPathName]. |
|
106 ^ pathName |
|
107 ! |
|
108 |
|
109 pathName:dirName |
|
110 "set my pathname; return nil if not a valid path; self otherwise" |
|
111 |
|
112 pathName := dirName. |
|
113 (dirName startsWith:'/') ifFalse:[ |
|
114 lazy := true |
|
115 ] ifTrue:[ |
|
116 (dirName includes:$.) ifTrue:[ |
|
117 lazy := true |
|
118 ] |
|
119 ]. |
|
120 ^ self |
|
121 " |
|
122 (OperatingSystem isDirectory:pathName) ifFalse:[^ nil] |
|
123 " |
|
124 ! |
|
125 |
|
126 contents |
|
127 "return a collection with all files and subdirectories in the receiver" |
|
128 |
|
129 |coll| |
|
130 |
|
131 coll := OrderedCollection new. |
|
132 self do:[:name | |
|
133 coll add:name |
|
134 ]. |
|
135 (coll size ~~ 0) ifTrue:[ |
|
136 coll sort |
|
137 ]. |
|
138 ^ coll |
|
139 ! |
|
140 |
|
141 directories |
|
142 "return a collection with all subdirectories in the receiver directory" |
|
143 |
|
144 |coll| |
|
145 |
|
146 coll := OrderedCollection new. |
|
147 self directoriesDo:[:name | |
|
148 coll add:name |
|
149 ]. |
|
150 (coll size ~~ 0) ifTrue:[ |
|
151 coll sort |
|
152 ]. |
|
153 ^ coll |
|
154 ! |
|
155 |
|
156 files |
|
157 "return a collection with all plain files in the receiver directory" |
|
158 |
|
159 |coll| |
|
160 |
|
161 coll := OrderedCollection new. |
|
162 self filesDo:[:name | |
|
163 coll add:name |
|
164 ]. |
|
165 ^ coll sort |
|
166 ! ! |
|
167 |
|
168 !FileDirectory methodsFor:'private'! |
|
169 |
|
170 getFullPathName |
|
171 "make my pathname be a full pathname - i.e. starting at root" |
|
172 |
|
173 |aStream command shortPathName fullPathName| |
|
174 |
|
175 (pathName = '/') ifTrue:[ |
|
176 lazy := false. |
|
177 ^ self |
|
178 ]. |
|
179 |
|
180 "since currentDirectory is used very often, cache its path here" |
|
181 |
|
182 (pathName = '.') ifTrue:[ |
|
183 pathOfCurrentDirectory notNil ifTrue:[ |
|
184 pathName := pathOfCurrentDirectory. |
|
185 lazy := false. |
|
186 ^ self |
|
187 ] |
|
188 ]. |
|
189 |
|
190 shortPathName := pathName. |
|
191 |
|
192 "sys5.4 and sunos have a convenient function for this ..." |
|
193 %{ |
|
194 #if defined(SYSV4) || defined(sunos) |
|
195 # include <stdlib.h> |
|
196 # include <sys/param.h> |
|
197 |
|
198 char nameBuffer[MAXPATHLEN + 1]; |
|
199 |
|
200 if (realpath(_stringVal(_INST(pathName)), nameBuffer)) { |
|
201 fullPathName = _MKSTRING(nameBuffer COMMA_CON); |
|
202 } |
|
203 #endif |
|
204 %} |
|
205 . |
|
206 fullPathName notNil ifTrue:[ |
|
207 pathName := fullPathName. |
|
208 lazy := false |
|
209 ] ifFalse:[ |
|
210 "since there might be symbolic links and other stuff involved, |
|
211 better trust pwd than removing '..' by ourself |
|
212 - although this is very slow" |
|
213 |
|
214 command := 'cd ' , pathName , '; pwd'. |
|
215 aStream := PipeStream readingFrom:command. |
|
216 aStream isNil ifFalse:[ |
|
217 (aStream atEnd) ifFalse:[ |
|
218 fullPathName := aStream nextLine |
|
219 ]. |
|
220 aStream close. |
|
221 fullPathName notNil ifTrue:[ |
|
222 pathName := fullPathName. |
|
223 lazy := false |
|
224 ] |
|
225 ] ifTrue:[ |
|
226 self error:('PipeStream for <' , command , '> failed'). |
|
227 "by clearing lazy, we avoid triggering the error again" |
|
228 lazy := false |
|
229 ] |
|
230 ]. |
|
231 |
|
232 "if it was the current dir, keep name for next query" |
|
233 (shortPathName = '.') ifTrue:[ |
|
234 pathOfCurrentDirectory := fullPathName |
|
235 ] |
|
236 ! ! |
|
237 |
|
238 !FileDirectory methodsFor:'basic'! |
|
239 |
|
240 createDirectory:newName |
|
241 "create a new filedirectory as a subdirectory of myself; |
|
242 return true if successful" |
|
243 |
|
244 |realName| |
|
245 |
|
246 (newName = '.') ifFalse:[ |
|
247 (newName = '..') ifFalse:[ |
|
248 ((newName at:1) == $/) ifTrue:[ |
|
249 realName := newName copyFrom:2 |
|
250 ] ifFalse:[ |
|
251 realName := newName |
|
252 ]. |
|
253 (realName startsWith:'/') ifTrue:[ |
|
254 ^ OperatingSystem createDirectory:realName |
|
255 ] ifFalse:[ |
|
256 ^ OperatingSystem createDirectory:(pathName , '/' , realName) |
|
257 ] |
|
258 ] |
|
259 ]. |
|
260 ^ false |
|
261 ! |
|
262 |
|
263 removeFile:fileName |
|
264 "remove the file 'fileName' from myself; return true if successful" |
|
265 |
|
266 (fileName startsWith:'/') ifTrue:[ |
|
267 ^ OperatingSystem removeFile:fileName |
|
268 ]. |
|
269 ^ OperatingSystem removeFile:(pathName , '/' , fileName) |
|
270 ! |
|
271 |
|
272 removeDirectory:dirName |
|
273 "remove the directory 'dirName' from myself; return true if successful" |
|
274 |
|
275 (dirName startsWith:'/') ifTrue:[ |
|
276 ^ OperatingSystem removeDirectory:dirName |
|
277 ]. |
|
278 ^ OperatingSystem removeDirectory:(pathName , '/' , dirName) |
|
279 ! |
|
280 |
|
281 remove:aFileOrDirectoryName |
|
282 "remove the file or directory from myself; return true if successful" |
|
283 |
|
284 |path| |
|
285 |
|
286 (aFileOrDirectoryName startsWith:'/') ifTrue:[ |
|
287 path := aFileOrDirectoryName |
|
288 ] ifFalse:[ |
|
289 path := (pathName , '/' , aFileOrDirectoryName) |
|
290 ]. |
|
291 (OperatingSystem isDirectory:path) ifTrue:[ |
|
292 ^ OperatingSystem removeDirectory:path |
|
293 ]. |
|
294 ^ OperatingSystem removeFile:path |
|
295 ! |
|
296 |
|
297 link:oldFileName to:newFileName |
|
298 "link oldFileName to newFileName in myself, return true if successful" |
|
299 |
|
300 |path1 path2| |
|
301 |
|
302 (oldFileName startsWith:'/') ifTrue:[ |
|
303 path1 := oldFileName |
|
304 ] ifFalse:[ |
|
305 path1 := (pathName , '/' , oldFileName) |
|
306 ]. |
|
307 (newFileName startsWith:'/') ifTrue:[ |
|
308 path2 := newFileName |
|
309 ] ifFalse:[ |
|
310 path2 := (pathName , '/' , newFileName) |
|
311 ]. |
|
312 ^ OperatingSystem link:path1 to:path2 |
|
313 ! |
|
314 |
|
315 renameFile:oldFileName newName:newFileName |
|
316 "rename the file; return true if successful" |
|
317 |
|
318 |path1 path2| |
|
319 |
|
320 (oldFileName startsWith:'/') ifTrue:[ |
|
321 path1 := oldFileName |
|
322 ] ifFalse:[ |
|
323 path1 := (pathName , '/' , oldFileName) |
|
324 ]. |
|
325 (newFileName startsWith:'/') ifTrue:[ |
|
326 path2 := newFileName |
|
327 ] ifFalse:[ |
|
328 path2 := (pathName , '/' , newFileName) |
|
329 ]. |
|
330 ^ OperatingSystem rename:path1 to:path2 |
|
331 ! ! |
|
332 |
|
333 !FileDirectory methodsFor:'queries'! |
|
334 |
|
335 id |
|
336 "return the directories file-id (inode number)" |
|
337 |
|
338 ^ OperatingSystem idOf:pathName |
|
339 ! |
|
340 |
|
341 exists |
|
342 "return true if this directory exists" |
|
343 |
|
344 ^ OperatingSystem isDirectory:pathName |
|
345 "(FileDirectory directoryNamed:'fooBar') exists" |
|
346 ! |
|
347 |
|
348 infoOf:name |
|
349 "return an array filled with file info for the file 'aFileName'; |
|
350 return nil if such a file does not exist" |
|
351 |
|
352 (name startsWith:'/') ifTrue:[ |
|
353 ^ OperatingSystem infoOf:name |
|
354 ]. |
|
355 ^ OperatingSystem infoOf:(pathName , '/' , name) |
|
356 ! |
|
357 |
|
358 timeOfLastChange:name |
|
359 "return the timeStamp of a file in myself" |
|
360 |
|
361 (name startsWith:'/') ifTrue:[ |
|
362 ^ OperatingSystem timeOfLastChange:name |
|
363 ]. |
|
364 ^ OperatingSystem timeOfLastChange:(pathName , '/' , name) |
|
365 ! |
|
366 |
|
367 timeOfLastChange |
|
368 "return the timeStamp of myself" |
|
369 |
|
370 ^ OperatingSystem timeOfLastChange:pathName |
|
371 ! |
|
372 |
|
373 accessModeOf:aFileName |
|
374 "return the access-mode bits (rwxrwxrwx) of a file in myself" |
|
375 |
|
376 (aFileName startsWith:'/') ifTrue:[ |
|
377 ^ OperatingSystem accessModeOf:aFileName |
|
378 ]. |
|
379 ^ OperatingSystem accessModeOf:(pathName , '/' , aFileName) |
|
380 ! |
|
381 |
|
382 changeAccessModeOf:aFileName to:modeBits |
|
383 "set the access-mode bits (rwxrwxrwx) of a file in myself" |
|
384 |
|
385 (aFileName startsWith:'/') ifTrue:[ |
|
386 ^ OperatingSystem changeAccessModeOf:aFileName |
|
387 to:modeBits |
|
388 ]. |
|
389 ^ OperatingSystem changeAccessModeOf:(pathName , '/' , aFileName) |
|
390 to:modeBits |
|
391 ! |
|
392 |
|
393 typeOf:aFileName |
|
394 "return the symbolic type of a file in myself" |
|
395 |
|
396 (aFileName startsWith:'/') ifTrue:[ |
|
397 ^ OperatingSystem typeOf:aFileName |
|
398 ]. |
|
399 ^ OperatingSystem typeOf:(pathName , '/' , aFileName) |
|
400 ! |
|
401 |
|
402 isDirectory:name |
|
403 "return true, if the given name is that of a directory in myself" |
|
404 |
|
405 (name startsWith:'/') ifTrue:[ |
|
406 ^ OperatingSystem isDirectory:name |
|
407 ]. |
|
408 ^ OperatingSystem isDirectory:(pathName , '/' , name) |
|
409 ! |
|
410 |
|
411 isReadable:name |
|
412 "return true, if the given file is readable" |
|
413 |
|
414 (name startsWith:'/') ifTrue:[ |
|
415 ^ OperatingSystem isReadable:name |
|
416 ]. |
|
417 ^ OperatingSystem isReadable:(pathName , '/' , name) |
|
418 ! |
|
419 |
|
420 isWritable:name |
|
421 "return true, if the given file is readable" |
|
422 |
|
423 (name startsWith:'/') ifTrue:[ |
|
424 ^ OperatingSystem isWritable:name |
|
425 ]. |
|
426 ^ OperatingSystem isWritable:(pathName , '/' , name) |
|
427 ! |
|
428 |
|
429 isExecutable:name |
|
430 "return true, if the given file is executable" |
|
431 |
|
432 (name startsWith:'/') ifTrue:[ |
|
433 ^ OperatingSystem isExecutable:name |
|
434 ]. |
|
435 ^ OperatingSystem isExecutable:(pathName , '/' , name) |
|
436 ! ! |
|
437 |
|
438 !FileDirectory methodsFor:'printing & storing'! |
|
439 |
|
440 printString |
|
441 lazy ifTrue:[self getFullPathName]. |
|
442 ^ '(a FileDirectory pathName:' , pathName, ')' |
|
443 ! |
|
444 |
|
445 storeOn:aStream |
|
446 lazy ifTrue:[self getFullPathName]. |
|
447 aStream nextPutAll:'(FileDirectory directoryNamed:'. |
|
448 aStream nextPutAll:pathName. |
|
449 aStream nextPut:$) |
|
450 ! ! |
|
451 |
|
452 !FileDirectory methodsFor:'more instance creation'! |
|
453 |
|
454 directoryNamed:aName |
|
455 ^ self class directoryNamed:aName in:self pathName |
|
456 ! ! |
|
457 |
|
458 !FileDirectory methodsFor:'enumerating'! |
|
459 |
|
460 where:testBlock do:aBlock |
|
461 "evaluate the argument, aBlock for every object in the directory |
|
462 for which testBlock evaluates to true." |
|
463 |
|
464 |aStream name| |
|
465 |
|
466 aStream := DirectoryStream directoryNamed:pathName. |
|
467 aStream isNil ifTrue:[^ nil]. |
|
468 [aStream atEnd] whileFalse:[ |
|
469 name := aStream nextLine. |
|
470 name notNil ifTrue:[ |
|
471 (testBlock value:name) ifTrue:[ |
|
472 aBlock value:name |
|
473 ] |
|
474 ] |
|
475 ]. |
|
476 aStream close |
|
477 ! |
|
478 |
|
479 do:aBlock |
|
480 "evaluate the argument, aBlock for every name in the directory" |
|
481 |
|
482 self where:[:name | true] do:aBlock |
|
483 ! |
|
484 |
|
485 namesDo:aBlock |
|
486 "evaluate the argument, aBlock for every name in the directory. |
|
487 for ST-80 compatibility" |
|
488 |
|
489 self do:aBlock |
|
490 ! |
|
491 |
|
492 filesDo:aBlock |
|
493 "evaluate the argument, aBlock for every plain file name in the directory" |
|
494 |
|
495 self where:[:name | (self isDirectory:name) not] do:aBlock |
|
496 ! |
|
497 |
|
498 directoriesDo:aBlock |
|
499 "evaluate the argument, aBlock for every subdirectory name in the directory" |
|
500 |
|
501 self where:[:name | (self isDirectory:name) ifTrue:[ |
|
502 ((name ~= '.') and:[name ~= '..']) |
|
503 ] ifFalse:[ |
|
504 false |
|
505 ] |
|
506 ] do:aBlock |
|
507 ! |
|
508 |
|
509 allFilesDo:aBlock |
|
510 "evaluate the argument, aBlock for every file name in the directory and in all |
|
511 subdirectories" |
|
512 |
|
513 |aStream command line| |
|
514 |
|
515 lazy ifTrue:[self getFullPathName]. |
|
516 command := 'cd ' , pathName , '; find . -print'. |
|
517 aStream := PipeStream readingFrom:command. |
|
518 aStream isNil ifTrue:[^ nil]. |
|
519 [aStream atEnd] whileFalse:[ |
|
520 line := aStream nextLine. |
|
521 line notNil ifTrue:[ |
|
522 (line = '.') ifFalse:[ |
|
523 "cut off initial ./" |
|
524 line := line copyFrom:3 to:(line size) |
|
525 ]. |
|
526 aBlock value:line |
|
527 ] |
|
528 ]. |
|
529 aStream close |
|
530 ! |
|
531 |
|
532 allDirectoriesDo:aBlock |
|
533 "evaluate the argument, aBlock for every directory name |
|
534 in the directory and in all subdirectories" |
|
535 |
|
536 |aStream command line| |
|
537 |
|
538 lazy ifTrue:[self getFullPathName]. |
|
539 command := 'cd ' , pathName , '; find . -type d -print'. |
|
540 aStream := PipeStream readingFrom:command. |
|
541 aStream isNil ifTrue:[^ nil]. |
|
542 [aStream atEnd] whileFalse:[ |
|
543 line := aStream nextLine. |
|
544 line notNil ifTrue:[ |
|
545 (line = '.') ifFalse:[ |
|
546 "cut off initial ./" |
|
547 line := line copyFrom:3 to:(line size) |
|
548 ]. |
|
549 aBlock value:line |
|
550 ] |
|
551 ]. |
|
552 aStream close |
|
553 ! ! |