2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
24 #if HAVE_UNISTD_H || __APPLE__
28 # include <sys/stat.h>
31 # include <sys/fcntl.h>
33 #if HAVE_FCNTL_H || __APPLE__
37 #include <NGStreams/NGFileStream.h>
38 #include <NGStreams/NGBufferedStream.h>
39 #include <NGStreams/NGConcreteStreamFileHandle.h>
40 #include <NGStreams/NGLockingStream.h>
41 #include <NGStreams/NGStreamExceptions.h>
42 #include <NGStreams/NGDescriptorFunctions.h>
44 #import <Foundation/NSThread.h>
46 #if !defined(POLLRDNORM)
47 # define POLLRDNORM POLLIN
50 // TODO: NGFileStream needs to be changed to operate without throwing
53 NGStreams_DECLARE NSString *NGFileReadOnly = @"r";
54 NGStreams_DECLARE NSString *NGFileWriteOnly = @"w";
55 NGStreams_DECLARE NSString *NGFileReadWrite = @"rw";
56 NGStreams_DECLARE NSString *NGFileAppend = @"a";
57 NGStreams_DECLARE NSString *NGFileReadAppend = @"ra";
59 static const int NGInvalidUnixDescriptor = -1;
60 static const int NGFileCreationMask = 0666; // rw-rw-rw-
62 @interface _NGConcreteFileStreamFileHandle : NGConcreteStreamFileHandle
65 NGStreams_DECLARE id<NGInputStream> NGIn = nil;
66 NGStreams_DECLARE id<NGOutputStream> NGOut = nil;
67 NGStreams_DECLARE id<NGOutputStream> NGErr = nil;
69 @implementation NGFileStream
73 #if defined(__MINGW32__)
74 - (id)__initWithInConsole {
75 if ((self = [self init])) {
76 self->systemPath = @"CONIN$";
77 self->streamMode = NGStreamMode_readWrite;
78 self->fh = GetStdHandle(STD_INPUT_HANDLE);
80 self->fh = CreateFile("CONIN$", GENERIC_READ, FILE_SHARE_READ,
89 - (id)__initWithOutConsole {
90 if ((self = [self init])) {
92 self->systemPath = @"CONOUT$";
93 self->streamMode = NGStreamMode_readWrite;
94 self->fh = GetStdHandle(STD_OUTPUT_HANDLE);
96 self->fh = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
102 FlushFileBuffers(self->fh);
107 - (id)__initWithDescriptor:(int)_fd mode:(NGStreamMode)_mode {
108 if ((self = [self init])) {
110 self->streamMode = _mode;
116 void NGInitStdio(void) {
117 static BOOL isInitialized = NO;
118 if (!isInitialized) {
119 NGFileStream *ti = nil, *to = nil, *te = nil;
123 #if defined(__MINGW32__)
124 ti = [[NGFileStream alloc] __initWithInConsole];
125 to = [[NGFileStream alloc] __initWithOutConsole];
128 ti = [[NGFileStream alloc] __initWithDescriptor:0
129 mode:NGStreamMode_readOnly];
130 to = [[NGFileStream alloc] __initWithDescriptor:1
131 mode:NGStreamMode_writeOnly];
132 te = [[NGFileStream alloc] __initWithDescriptor:2
133 mode:NGStreamMode_writeOnly];
136 NGIn = [[NGBufferedStream alloc] initWithSource:(id)ti];
137 NGOut = [[NGBufferedStream alloc] initWithSource:(id)to];
138 NGErr = [[NGBufferedStream alloc] initWithSource:(id)te];
140 [ti release]; ti = nil;
141 [to release]; to = nil;
142 [te release]; te = nil;
146 + (void)_makeThreadSafe:(NSNotification *)_notification {
147 NGLockingStream *li = nil, *lo = nil, *le = nil;
149 if ([NGIn isKindOfClass:[NGLockingStream class]])
152 li = [[NGLockingStream alloc] initWithSource:(id)NGIn];
153 [NGIn release]; NGIn = li;
154 lo = [[NGLockingStream alloc] initWithSource:(id)NGOut];
155 [NGOut release]; NGOut = lo;
156 le = [[NGLockingStream alloc] initWithSource:(id)NGErr];
157 [NGErr release]; NGErr = le;
160 + (void)_flushForExit:(NSNotification *)_notification {
166 static void _flushForExit(void) {
173 BOOL isInitialized = NO;
174 if (!isInitialized) {
177 if ([NSThread isMultiThreaded])
178 [self _makeThreadSafe:nil];
180 [[NSNotificationCenter defaultCenter]
182 selector:@selector(_makeThreadSafe:)
183 name:NSWillBecomeMultiThreadedNotification
186 atexit(_flushForExit);
190 /* normal file stream */
193 if ((self = [super init])) {
194 self->streamMode = NGStreamMode_undefined;
195 self->systemPath = nil;
196 self->markDelta = -1;
198 #if defined(__MINGW32__)
199 self->fh = INVALID_HANDLE_VALUE;
201 self->fd = NGInvalidUnixDescriptor;
207 - (id)initWithPath:(NSString *)_path {
208 if ((self = [self init])) {
209 self->systemPath = [_path copy];
214 - (id)initWithFileHandle:(NSFileHandle *)_handle {
215 if ((self = [self init])) {
216 #if defined(__MINGW32__)
217 self->fh = [_handle nativeHandle];
219 self->fd = [_handle fileDescriptor];
228 NSLog(@"NGFileStream(gcFinalize): closing %@", self);
235 self->streamMode = NGStreamMode_undefined;
236 [self->systemPath release]; self->systemPath = nil;
243 - (BOOL)openInMode:(NSString *)_mode {
245 // NGUnknownStreamModeException when _mode is invalid
246 // NGCouldNotOpenStreamException when the file could not be opened
247 #if defined(__MINGW32__)
251 if (self->fh != INVALID_HANDLE_VALUE)
252 [self close]; // if stream is open, close and reopen
254 if ([_mode isEqualToString:NGFileReadOnly]) {
255 self->streamMode = NGStreamMode_readOnly;
256 openFlags = GENERIC_READ;
257 shareMode = FILE_SHARE_READ;
259 else if ([_mode isEqualToString:NGFileWriteOnly]) {
260 self->streamMode = NGStreamMode_writeOnly;
261 openFlags = GENERIC_WRITE;
262 shareMode = FILE_SHARE_WRITE;
264 else if ([_mode isEqualToString:NGFileReadWrite]) {
265 self->streamMode = NGStreamMode_readWrite;
266 openFlags = GENERIC_READ | GENERIC_WRITE;
267 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
270 [[[NGUnknownStreamModeException alloc]
271 initWithStream:self mode:_mode] raise];
275 self->fh = CreateFile([self->systemPath fileSystemRepresentation],
276 openFlags, shareMode, NULL,
277 OPEN_ALWAYS, // same as the Unix O_CREAT flag
278 0, // security flags ?
281 if (self->fh == INVALID_HANDLE_VALUE)
282 [NGCouldNotOpenStreamException raiseWithStream:self];
285 int openFlags; // flags passed to open() call
287 if (self->fd != NGInvalidUnixDescriptor)
288 [self close]; // if stream is open, close and reopen
290 if ([_mode isEqualToString:NGFileReadOnly]) {
291 self->streamMode = NGStreamMode_readOnly;
292 openFlags = O_RDONLY;
294 else if ([_mode isEqualToString:NGFileWriteOnly]) {
295 self->streamMode = NGStreamMode_writeOnly;
296 openFlags = O_WRONLY | O_CREAT;
298 else if ([_mode isEqualToString:NGFileReadWrite]) {
299 self->streamMode = NGStreamMode_readWrite;
300 openFlags = O_RDWR | O_CREAT;
302 else if ([_mode isEqualToString:NGFileAppend]) {
303 self->streamMode = NGStreamMode_writeOnly;
304 openFlags = O_WRONLY | O_CREAT | O_APPEND;
306 else if ([_mode isEqualToString:NGFileReadAppend]) {
307 self->streamMode = NGStreamMode_readWrite;
308 openFlags = O_RDWR | O_CREAT | O_APPEND;
311 [[[NGUnknownStreamModeException alloc]
312 initWithStream:self mode:_mode] raise];
316 self->fd = open([self->systemPath fileSystemRepresentation],
320 if (self->fd == -1) {
321 self->fd = NGInvalidUnixDescriptor;
323 [NGCouldNotOpenStreamException raiseWithStream:self];
328 self->markDelta = -1; // not marked
333 #if defined(__MINGW32__)
334 return (self->fh != INVALID_HANDLE_VALUE) ? YES : NO;
336 return (self->fd != NGInvalidUnixDescriptor) ? YES : NO;
340 // Foundation file handles
342 - (void)resetFileHandle { // called by NSFileHandle on dealloc
345 - (NSFileHandle *)fileHandle {
346 if (self->handle == nil)
347 self->handle = [[_NGConcreteFileStreamFileHandle allocWithZone:[self zone]]
348 initWithStream:self];
349 return [self->handle autorelease];
352 #if defined(__MINGW32__)
353 - (HANDLE)windowsFileHandle {
358 - (int)fileDescriptor {
359 #if defined(__MINGW32__)
360 return (int)[self fileHandle];
368 static void _checkOpen(NGFileStream *self, NSString *_reason) {
369 #if defined(__MINGW32__)
370 if (self->fh == INVALID_HANDLE_VALUE)
371 [NGStreamNotOpenException raiseWithStream:self reason:_reason];
373 if (self->fd == NGInvalidUnixDescriptor)
374 [NGStreamNotOpenException raiseWithStream:self reason:_reason];
378 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
380 // NGWriteOnlyStreamException when the stream is not readable
381 // NGStreamNotOpenException when the stream is not open
382 // NGEndOfStreamException when the end of the stream is reached
383 // NGStreamReadErrorException when the read call failed
385 _checkOpen(self, @"tried to read from a file stream which is closed");
387 if (!NGCanReadInStreamMode(streamMode))
388 [NGWriteOnlyStreamException raiseWithStream:self];
391 #if defined(__MINGW32__)
392 DWORD readResult = 0;
394 if (ReadFile(self->fh, _buf, _len, &readResult, NULL) == FALSE) {
395 DWORD lastErr = GetLastError();
397 if (lastErr == ERROR_HANDLE_EOF)
398 [NGEndOfStreamException raiseWithStream:self];
400 [NGStreamReadErrorException raiseWithStream:self errorCode:lastErr];
403 [NGEndOfStreamException raiseWithStream:self];
409 readResult = read(self->fd, _buf, _len);
412 [NGEndOfStreamException raiseWithStream:self];
413 else if (readResult == -1) {
416 if (errCode == EINTR)
417 // system call was interrupted
420 [NGStreamReadErrorException raiseWithStream:self errorCode:errCode];
423 while ((readResult <= 0) && (retryCount < 10));
425 if (retryCount >= 10)
426 [NGStreamReadErrorException raiseWithStream:self errorCode:EINTR];
429 NSAssert(readResult > 0, @"invalid read method state");
432 if (self->markDelta != -1)
433 self->markDelta += readResult; // increase delta
439 - (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len {
441 // NGReadOnlyStreamException when the stream is not writeable
442 // NGStreamNotOpenException when the stream is not open
443 // NGStreamWriteErrorException when the write call failed
445 _checkOpen(self, @"tried to write to a file stream which is closed");
447 if (!NGCanWriteInStreamMode(streamMode))
448 [NGReadOnlyStreamException raiseWithStream:self];
451 #if defined(__MINGW32__)
452 DWORD writeResult = 0;
454 if (WriteFile(self->fh, _buf, _len, &writeResult, NULL) == FALSE) {
455 DWORD errorCode = GetLastError();
458 case ERROR_INVALID_HANDLE:
459 [NGStreamWriteErrorException raiseWithStream:self
460 reason:@"incorrect file handle"];
462 case ERROR_WRITE_PROTECT:
463 [NGStreamWriteErrorException raiseWithStream:self
464 reason:@"disk write protected"];
466 case ERROR_NOT_READY:
467 [NGStreamWriteErrorException raiseWithStream:self
468 reason:@"the drive is not ready"];
470 case ERROR_HANDLE_EOF:
471 [NGStreamWriteErrorException raiseWithStream:self
472 reason:@"reached end of file"];
474 case ERROR_DISK_FULL:
475 [NGStreamWriteErrorException raiseWithStream:self
476 reason:@"disk is full"];
480 [NGStreamWriteErrorException raiseWithStream:self
481 errorCode:GetLastError()];
484 NSLog(@"invalid program state, aborting");
492 writeResult = write(self->fd, _buf, _len);
494 if (writeResult == -1) {
497 if (errCode == EINTR)
498 // system call was interrupted
501 [NGStreamWriteErrorException raiseWithStream:self errorCode:errno];
504 while ((writeResult == -1) && (retryCount < 10));
506 if (retryCount >= 10)
507 [NGStreamWriteErrorException raiseWithStream:self errorCode:EINTR];
515 #if defined(__MINGW32__)
516 if (self->fh == INVALID_HANDLE_VALUE) {
517 NSLog(@"tried to close already closed stream %@", self);
518 return YES; /* not signaled as an error .. */
521 if (CloseHandle(self->fh) == FALSE) {
522 [NGCouldNotCloseStreamException raiseWithStream:self];
526 self->fh = INVALID_HANDLE_VALUE;
528 if (self->fd == NGInvalidUnixDescriptor) {
529 NSLog(@"tried to close already closed stream %@", self);
530 return YES; /* not signaled as an error .. */
533 if (close(self->fd) != 0) {
534 [NGCouldNotCloseStreamException raiseWithStream:self];
538 self->fd = NGInvalidUnixDescriptor;
540 self->markDelta = -1;
544 - (NGStreamMode)mode {
545 return self->streamMode;
547 - (BOOL)isRootStream {
551 #if defined(__MINGW32__)
553 if (self->fh != INVALID_HANDLE_VALUE)
554 FlushFileBuffers(self->fh);
561 #if defined(__MINGW32__)
562 - (BOOL)wouldBlockInMode:(NGStreamMode)_mode {
563 NSLog(@"%@ not supported in Windows environment !",
564 NSStringFromSelector(_cmd));
568 - (BOOL)wouldBlockInMode:(NGStreamMode)_mode {
571 if (self->fd == NGInvalidUnixDescriptor)
574 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM;
575 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM;
577 // timeout of 0 means return immediatly
578 return (NGPollDescriptor(self->fd, events, 0) == 1 ? NO : YES);
589 if (![self moveByOffset:-(self->markDelta)])
591 self->markDelta = -1;
594 - (BOOL)markSupported {
598 // NGPositionableStream
600 - (BOOL)moveToLocation:(unsigned)_location {
601 self->markDelta = -1;
603 #if defined(__MINGW32__)
604 if (SetFilePointer(self->fh, _location, NULL, FILE_BEGIN) == -1) {
605 [NGStreamSeekErrorException raiseWithStream:self errorCode:GetLastError()];
609 if (lseek(self->fd, _location, SEEK_SET) == -1) {
610 [NGStreamSeekErrorException raiseWithStream:self errorCode:errno];
616 - (BOOL)moveByOffset:(int)_delta {
617 self->markDelta += _delta;
619 #if defined(__MINGW32__)
620 if (SetFilePointer(self->fh, _delta, NULL, FILE_CURRENT) == -1) {
621 [NGStreamSeekErrorException raiseWithStream:self errorCode:GetLastError()];
625 if (lseek(self->fd, _delta, SEEK_CUR) == -1) {
626 [NGStreamSeekErrorException raiseWithStream:self errorCode:errno];
635 - (NSString *)description {
636 return [NSString stringWithFormat:
637 @"<%@[0x%p] path=%@ mode=%@>",
638 NSStringFromClass([self class]), (unsigned)self,
639 self->systemPath ? self->systemPath : (NSString *)@"nil",
640 [self modeDescription]];
643 @end /* NGFileStream */
646 @implementation _NGConcreteFileStreamFileHandle
650 #if defined(__MINGW32__)
651 - (HANDLE)fileHandle {
652 return [(NGFileStream *)stream windowsFileHandle];
656 - (int)fileDescriptor {
657 return [(NGFileStream *)stream fileDescriptor];