|
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 ExternalStream subclass:#FileStream |
|
14 instanceVariableNames:'pathName' |
|
15 classVariableNames:'' |
|
16 poolDictionaries:'' |
|
17 category:'Streams-External' |
|
18 ! |
|
19 |
|
20 FileStream comment:' |
|
21 COPYRIGHT (c) 1989-93 by Claus Gittinger |
|
22 All Rights Reserved |
|
23 |
|
24 This class provides access to the operating systems underlying file |
|
25 system (i.e. its an interface to the stdio library). |
|
26 |
|
27 %W% %E% |
|
28 '! |
|
29 |
|
30 %{ |
|
31 #include <stdio.h> |
|
32 |
|
33 #ifdef transputer |
|
34 # include <iocntrl.h> |
|
35 # ifndef fileno |
|
36 /* kludge: inmos forgot fileno */ |
|
37 # define fileno(f) ((f)->__file) |
|
38 # endif |
|
39 #else |
|
40 # include <sys/types.h> |
|
41 # include <sys/stat.h> |
|
42 #endif |
|
43 %} |
|
44 |
|
45 !FileStream class methodsFor:'instance creation'! |
|
46 |
|
47 newFileNamed:filename |
|
48 "return a FileStream for new file named filename, aString. |
|
49 If the file exists, it is truncated, otherwise created. |
|
50 The file is opened for write access only." |
|
51 |
|
52 |newStream| |
|
53 newStream := (self basicNew) pathName:filename. |
|
54 newStream createForWriting isNil ifTrue:[^nil]. |
|
55 ^ newStream |
|
56 ! |
|
57 |
|
58 newFileNamed:filename in:aDirectory |
|
59 "return a FileStream for new file named filename, aString |
|
60 in aDirectory, a FileDirectory. |
|
61 If the file exists, it is truncated, otherwise created. |
|
62 The file is opened for write access only." |
|
63 |
|
64 |newStream| |
|
65 newStream := (self basicNew) pathName:filename in:aDirectory. |
|
66 newStream createForWriting isNil ifTrue:[^nil]. |
|
67 ^ newStream |
|
68 ! |
|
69 |
|
70 oldFileNamed:filename |
|
71 "return a FileStream for existing file named filename, aString. |
|
72 The file is opened for read/write access." |
|
73 |
|
74 |newStream| |
|
75 |
|
76 (OperatingSystem isReadable:filename) ifFalse:[^nil]. |
|
77 newStream := (self basicNew) pathName:filename. |
|
78 newStream readwrite. |
|
79 newStream openForReadWrite isNil ifTrue:[^nil]. |
|
80 newStream readLimit:(newStream size). |
|
81 ^ newStream |
|
82 ! |
|
83 |
|
84 oldFileNamed:filename in:aDirectory |
|
85 "return a FileStream for existing file named filename, aString |
|
86 in aDirectory, a FileDirectory. |
|
87 The file is opened for read/write access." |
|
88 |
|
89 |newStream| |
|
90 newStream := (self basicNew) pathName:filename in:aDirectory. |
|
91 newStream openForReadWrite isNil ifTrue:[^nil]. |
|
92 newStream readLimit:(newStream size). |
|
93 ^ newStream |
|
94 ! |
|
95 |
|
96 fileNamed:filename |
|
97 "return a stream on file filename - if the file does not |
|
98 already exist, create it." |
|
99 |
|
100 |stream| |
|
101 |
|
102 stream := self oldFileNamed:filename. |
|
103 stream isNil ifTrue:[ |
|
104 stream := self newFileNamed:filename |
|
105 ]. |
|
106 ^ stream |
|
107 ! |
|
108 |
|
109 fileNamed:filename in:aDirectory |
|
110 "return a stream on file filename - if the file does not |
|
111 already exist, create it." |
|
112 |
|
113 |stream| |
|
114 |
|
115 stream := self oldFileNamed:filename in:aDirectory. |
|
116 stream isNil ifTrue:[ |
|
117 stream := self newFileNamed:filename in:aDirectory |
|
118 ]. |
|
119 ^ stream |
|
120 ! |
|
121 |
|
122 readonlyFileNamed:filename |
|
123 "return a readonly FileStream for existing file named filename, aString" |
|
124 |
|
125 |newStream| |
|
126 |
|
127 (OperatingSystem isReadable:filename) ifFalse:[^nil]. |
|
128 |
|
129 newStream := (self basicNew) pathName:filename. |
|
130 newStream openForReading isNil ifTrue:[^nil]. |
|
131 newStream readLimit:(newStream size). |
|
132 ^ newStream |
|
133 ! |
|
134 |
|
135 readonlyFileNamed:filename in:aDirectory |
|
136 "return a readonly FileStream for existing file named filename, aString |
|
137 in aDirectory, a FileDirectory" |
|
138 |
|
139 |newStream| |
|
140 newStream := (self basicNew) pathName:filename in:aDirectory. |
|
141 newStream openForReading isNil ifTrue:[^nil]. |
|
142 newStream readLimit:(newStream size). |
|
143 ^ newStream |
|
144 ! |
|
145 |
|
146 appendingOldFileNamed:filename |
|
147 "return a FileStream for existing file named filename, aString" |
|
148 |
|
149 |newStream| |
|
150 newStream := (self basicNew) pathName:filename. |
|
151 newStream openForAppending isNil ifTrue:[^nil]. |
|
152 newStream readLimit:(newStream size). |
|
153 ^ newStream |
|
154 ! |
|
155 |
|
156 appendingOldFileNamed:filename in:aDirectory |
|
157 "return a FileStream for existing file named filename, aString |
|
158 in aDirectory, a FileDirectory" |
|
159 |
|
160 |newStream| |
|
161 newStream := (self basicNew) pathName:filename in:aDirectory. |
|
162 newStream openForAppending isNil ifTrue:[^nil]. |
|
163 newStream readLimit:(newStream size). |
|
164 ^ newStream |
|
165 ! ! |
|
166 |
|
167 !FileStream methodsFor:'accessing'! |
|
168 |
|
169 store:something |
|
170 "what really should this do" |
|
171 |
|
172 self nextPutAll:something |
|
173 ! |
|
174 |
|
175 directoryName |
|
176 "return the name of the directory I'm in" |
|
177 |
|
178 |path lastIndex index| |
|
179 |
|
180 path := pathName. |
|
181 lastIndex := 0. |
|
182 index := path indexOf:$/. |
|
183 [index ~~ 0] whileTrue:[ |
|
184 lastIndex := index. |
|
185 index := path indexOf:$/ startingAt:(index + 1) |
|
186 ]. |
|
187 (lastIndex == 0) ifTrue:[^ '.']. |
|
188 (lastIndex == 1) ifTrue:[^ '/']. |
|
189 ^ path copyFrom:1 to:(lastIndex - 1) |
|
190 ! |
|
191 |
|
192 name |
|
193 "return my name without leading direcory-path" |
|
194 |
|
195 |lastIndex index| |
|
196 |
|
197 lastIndex := 1. |
|
198 [true] whileTrue:[ |
|
199 index := pathName indexOf:$/ startingAt:lastIndex. |
|
200 (index == 0) ifTrue:[ |
|
201 ^ pathName copyFrom:lastIndex |
|
202 ]. |
|
203 lastIndex := index + 1 |
|
204 ] |
|
205 ! |
|
206 |
|
207 pathName |
|
208 "return the pathname" |
|
209 |
|
210 ^ pathName |
|
211 ! ! |
|
212 |
|
213 !FileStream methodsFor:'private'! |
|
214 |
|
215 pathName:filename |
|
216 "set the pathname" |
|
217 |
|
218 pathName := filename |
|
219 ! |
|
220 |
|
221 pathName:filename in:aDirectory |
|
222 "set the pathname starting at aDirectory, a FileDirectory" |
|
223 |
|
224 ((filename at:1) == $/) ifTrue:[ |
|
225 "filename may not start with a '/'" |
|
226 pathName := nil |
|
227 ] ifFalse:[ |
|
228 pathName := aDirectory pathName. |
|
229 (pathName endsWith:'/') ifFalse:[ |
|
230 pathName := pathName , '/' |
|
231 ]. |
|
232 pathName := pathName , filename |
|
233 ] |
|
234 ! |
|
235 |
|
236 open |
|
237 "open the file" |
|
238 |
|
239 pathName isNil ifTrue:[^nil]. |
|
240 (mode == #readonly) ifTrue: [ |
|
241 ^ self openForReading |
|
242 ]. |
|
243 (mode == #writeonly) ifTrue: [ |
|
244 ^ self openForWriting |
|
245 ]. |
|
246 ^ self openForReadWrite |
|
247 ! |
|
248 |
|
249 openWithMode:openmode |
|
250 "open the file; openmode is the string defining the way to open" |
|
251 |
|
252 |retVal| |
|
253 %{ |
|
254 FILE *f; |
|
255 OBJ path; |
|
256 extern OBJ ErrorNumber, Filename; |
|
257 extern errno; |
|
258 |
|
259 if (_INST(filePointer) == nil) { |
|
260 path = _INST(pathName); |
|
261 if (_isString(path) || (_Class(path) == Filename)) { |
|
262 f = (FILE *)fopen((char *) _stringVal(path), (char *) _stringVal(openmode)); |
|
263 if (f == NULL) { |
|
264 ErrorNumber = _MKSMALLINT(errno); |
|
265 _INST(position) = nil; |
|
266 } else { |
|
267 _INST(filePointer) = _MKSMALLINT((int)f); |
|
268 _INST(position) = _MKSMALLINT(1); |
|
269 retVal = self; |
|
270 } |
|
271 } |
|
272 } |
|
273 %} |
|
274 . |
|
275 retVal notNil ifTrue:[ |
|
276 lobby register:self |
|
277 ]. |
|
278 ^ retVal |
|
279 ! |
|
280 |
|
281 openForReading |
|
282 "open the file for readonly" |
|
283 |
|
284 mode := #readonly. |
|
285 ^ self openWithMode:'r' |
|
286 ! |
|
287 |
|
288 openForWriting |
|
289 "open the file for writeonly" |
|
290 |
|
291 mode := #writeonly. |
|
292 ^ self openWithMode:'w' |
|
293 ! |
|
294 |
|
295 openForAppending |
|
296 "open the file for writeonly appending to the end" |
|
297 |
|
298 mode := #writeonly. |
|
299 ^ self openWithMode:'a+' |
|
300 ! |
|
301 |
|
302 createForWriting |
|
303 "create/truncate the file for writeonly" |
|
304 |
|
305 mode := #writeonly. |
|
306 ^ self openWithMode:'w+' |
|
307 ! |
|
308 |
|
309 openForReadWrite |
|
310 "open the file for read/write" |
|
311 |
|
312 mode := #readwrite. |
|
313 ^ self openWithMode:'r+w' |
|
314 ! |
|
315 |
|
316 createForReadWrite |
|
317 "create/truncate the file for read/write" |
|
318 |
|
319 mode := #readwrite. |
|
320 ^ self openWithMode:'rw+' |
|
321 ! |
|
322 |
|
323 reOpen |
|
324 "sent after snapin to reopen streams" |
|
325 |
|
326 filePointer notNil ifTrue:[ |
|
327 "it was open, when snapped-out" |
|
328 filePointer := nil. |
|
329 self open. |
|
330 filePointer isNil ifTrue:[ |
|
331 Transcript showCr:('could not reopen file: ', pathName) |
|
332 ] ifFalse:[ |
|
333 self position:position |
|
334 ] |
|
335 ] |
|
336 ! |
|
337 |
|
338 size |
|
339 "return the size in bytes of the file" |
|
340 |
|
341 %{ /* NOCONTEXT */ |
|
342 |
|
343 #ifdef transputer |
|
344 FILE *f; |
|
345 int size; |
|
346 |
|
347 if (_INST(filePointer) != nil) { |
|
348 f = (FILE *)_intVal(_INST(filePointer)); |
|
349 if ((size = filesize(fileno(f))) >= 0) { |
|
350 RETURN ( _MKSMALLINT(size) ); |
|
351 } |
|
352 } |
|
353 #else |
|
354 FILE *f; |
|
355 struct stat buf; |
|
356 |
|
357 if (_INST(filePointer) != nil) { |
|
358 f = (FILE *)_intVal(_INST(filePointer)); |
|
359 if (fstat(fileno(f), &buf) >= 0) { |
|
360 RETURN ( _MKSMALLINT(buf.st_size) ); |
|
361 } |
|
362 } |
|
363 #endif |
|
364 %} |
|
365 . |
|
366 "could add a fall-back here: |
|
367 |
|
368 oldPosition := self position. |
|
369 self setToEnd. |
|
370 sz := self position. |
|
371 self position:oldPosition. |
|
372 ^ sz |
|
373 " |
|
374 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
375 ^ self primitiveFailed |
|
376 ! |
|
377 |
|
378 position |
|
379 "return the read/write position in the file - |
|
380 notice, in smalltalk indices start at 1 so begin of file is 1"" |
|
381 |
|
382 %{ /* NOCONTEXT */ |
|
383 |
|
384 FILE *f; |
|
385 long currentPosition; |
|
386 |
|
387 if (_INST(filePointer) != nil) { |
|
388 f = (FILE *)_intVal(_INST(filePointer)); |
|
389 currentPosition = ftell(f); |
|
390 if (currentPosition >= 0) { |
|
391 /* |
|
392 * notice: Smalltalk index starts at 1 |
|
393 */ |
|
394 RETURN ( _MKSMALLINT(currentPosition + 1) ); |
|
395 } |
|
396 } |
|
397 %} |
|
398 . |
|
399 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
400 ^ self primitiveFailed |
|
401 ! |
|
402 |
|
403 position:newPos |
|
404 "set the read/write position in the file" |
|
405 |
|
406 %{ /* NOCONTEXT */ |
|
407 |
|
408 FILE *f; |
|
409 extern OBJ ErrorNumber; |
|
410 extern errno; |
|
411 |
|
412 if (_INST(filePointer) != nil) { |
|
413 if (_isSmallInteger(newPos)) { |
|
414 f = (FILE *)_intVal(_INST(filePointer)); |
|
415 /* |
|
416 * notice: Smalltalk index starts at 1 |
|
417 */ |
|
418 if (fseek(f, _intVal(newPos) - 1, 0) >= 0) { |
|
419 _INST(position) = newPos; |
|
420 RETURN ( self ); |
|
421 } |
|
422 ErrorNumber = _MKSMALLINT(errno); |
|
423 } |
|
424 } |
|
425 %} |
|
426 . |
|
427 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
428 ^ self primitiveFailed |
|
429 ! |
|
430 |
|
431 setToEnd |
|
432 "set the read/write position in the file to be at the end of the file" |
|
433 |
|
434 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
435 %{ |
|
436 FILE *f; |
|
437 extern OBJ ErrorNumber; |
|
438 extern errno; |
|
439 |
|
440 f = (FILE *)_intVal(_INST(filePointer)); |
|
441 _INST(position) = nil; |
|
442 if (fseek(f, 0, 2) >= 0) { |
|
443 RETURN ( self ); |
|
444 } |
|
445 ErrorNumber = _MKSMALLINT(errno); |
|
446 %} |
|
447 . |
|
448 ^ self primitiveFailed |
|
449 ! ! |
|
450 |
|
451 !FileStream methodsFor:'printing & storing'! |
|
452 |
|
453 printOn:aStream |
|
454 aStream nextPutAll:'(a FileStream for:'. |
|
455 aStream nextPutAll:pathName. |
|
456 aStream nextPut:$) |
|
457 ! |
|
458 |
|
459 storeOn:aStream |
|
460 aStream nextPutAll:'(FileStream oldFileNamed:'. |
|
461 aStream nextPutAll:pathName. |
|
462 (self position ~~ 1) ifTrue:[ |
|
463 aStream nextPutAll:'; position:'. |
|
464 self position storeOn:aStream |
|
465 ]. |
|
466 aStream nextPut:$) |
|
467 ! ! |