2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
25 #if HAVE_UNISTD_H || __APPLE__
29 # include <sys/stat.h>
32 # include <sys/fcntl.h>
34 #if HAVE_FCNTL_H || __APPLE__
39 #import <Foundation/NSThread.h>
40 #include "NGFileStream.h"
41 #include "NGBufferedStream.h"
42 #include "NGConcreteStreamFileHandle.h"
43 #include "NGLockingStream.h"
44 #include "NGStreamExceptions.h"
45 #include "NGDescriptorFunctions.h"
47 #if !defined(POLLRDNORM)
48 # define POLLRDNORM POLLIN
51 // TODO: NGFileStream needs to be changed to operate without throwing
54 NGStreams_DECLARE NSString *NGFileReadOnly = @"r";
55 NGStreams_DECLARE NSString *NGFileWriteOnly = @"w";
56 NGStreams_DECLARE NSString *NGFileReadWrite = @"rw";
57 NGStreams_DECLARE NSString *NGFileAppend = @"a";
58 NGStreams_DECLARE NSString *NGFileReadAppend = @"ra";
60 static const int NGInvalidUnixDescriptor = -1;
61 static const int NGFileCreationMask = 0666; // rw-rw-rw-
63 @interface _NGConcreteFileStreamFileHandle : NGConcreteStreamFileHandle
66 NGStreams_DECLARE id<NGInputStream> NGIn = nil;
67 NGStreams_DECLARE id<NGOutputStream> NGOut = nil;
68 NGStreams_DECLARE id<NGOutputStream> NGErr = nil;
70 @implementation NGFileStream
74 #if defined(__MINGW32__)
75 - (id)__initWithInConsole {
76 if ((self = [self init])) {
77 self->systemPath = @"CONIN$";
78 self->streamMode = NGStreamMode_readWrite;
79 self->fh = GetStdHandle(STD_INPUT_HANDLE);
81 self->fh = CreateFile("CONIN$", GENERIC_READ, FILE_SHARE_READ,
90 - (id)__initWithOutConsole {
91 if ((self = [self init])) {
93 self->systemPath = @"CONOUT$";
94 self->streamMode = NGStreamMode_readWrite;
95 self->fh = GetStdHandle(STD_OUTPUT_HANDLE);
97 self->fh = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
103 FlushFileBuffers(self->fh);
108 - (id)__initWithDescriptor:(int)_fd mode:(NGStreamMode)_mode {
109 if ((self = [self init])) {
111 self->streamMode = _mode;
117 void NGInitStdio(void) {
118 static BOOL isInitialized = NO;
119 if (!isInitialized) {
120 NGFileStream *ti = nil, *to = nil, *te = nil;
124 #if defined(__MINGW32__)
125 ti = [[NGFileStream alloc] __initWithInConsole];
126 to = [[NGFileStream alloc] __initWithOutConsole];
129 ti = [[NGFileStream alloc] __initWithDescriptor:0 mode:NGStreamMode_readOnly];
130 to = [[NGFileStream alloc] __initWithDescriptor:1 mode:NGStreamMode_writeOnly];
131 te = [[NGFileStream alloc] __initWithDescriptor:2 mode:NGStreamMode_writeOnly];
134 NGIn = [[NGBufferedStream alloc] initWithSource:(id)ti];
135 NGOut = [[NGBufferedStream alloc] initWithSource:(id)to];
136 NGErr = [[NGBufferedStream alloc] initWithSource:(id)te];
138 [ti release]; ti = nil;
139 [to release]; to = nil;
140 [te release]; te = nil;
144 + (void)_makeThreadSafe:(NSNotification *)_notification {
145 NGLockingStream *li = nil, *lo = nil, *le = nil;
147 if ([NGIn isKindOfClass:[NGLockingStream class]])
150 li = [[NGLockingStream alloc] initWithSource:NGIn]; [NGIn release]; NGIn = li;
151 lo = [[NGLockingStream alloc] initWithSource:NGOut]; [NGOut release]; NGOut = lo;
152 le = [[NGLockingStream alloc] initWithSource:NGErr]; [NGErr release]; NGErr = le;
155 + (void)_flushForExit:(NSNotification *)_notification {
161 static void _flushForExit(void) {
168 BOOL isInitialized = NO;
169 if (!isInitialized) {
172 if ([NSThread isMultiThreaded])
173 [self _makeThreadSafe:nil];
175 [[NSNotificationCenter defaultCenter]
177 selector:@selector(_makeThreadSafe:)
178 name:NSWillBecomeMultiThreadedNotification
181 atexit(_flushForExit);
185 /* normal file stream */
188 if ((self = [super init])) {
189 self->streamMode = NGStreamMode_undefined;
190 self->systemPath = nil;
191 self->markDelta = -1;
193 #if defined(__MINGW32__)
194 self->fh = INVALID_HANDLE_VALUE;
196 self->fd = NGInvalidUnixDescriptor;
202 - (id)initWithPath:(NSString *)_path {
203 if ((self = [self init])) {
204 self->systemPath = [_path copy];
209 - (id)initWithFileHandle:(NSFileHandle *)_handle {
210 if ((self = [self init])) {
211 #if defined(__MINGW32__)
212 self->fh = [_handle nativeHandle];
214 self->fd = [_handle fileDescriptor];
223 NSLog(@"NGFileStream(gcFinalize): closing %@", self);
230 self->streamMode = NGStreamMode_undefined;
231 [self->systemPath release]; self->systemPath = nil;
238 - (BOOL)openInMode:(NSString *)_mode {
240 // NGUnknownStreamModeException when _mode is invalid
241 // NGCouldNotOpenStreamException when the file could not be opened
242 #if defined(__MINGW32__)
246 if (self->fh != INVALID_HANDLE_VALUE)
247 [self close]; // if stream is open, close and reopen
249 if ([_mode isEqualToString:NGFileReadOnly]) {
250 self->streamMode = NGStreamMode_readOnly;
251 openFlags = GENERIC_READ;
252 shareMode = FILE_SHARE_READ;
254 else if ([_mode isEqualToString:NGFileWriteOnly]) {
255 self->streamMode = NGStreamMode_writeOnly;
256 openFlags = GENERIC_WRITE;
257 shareMode = FILE_SHARE_WRITE;
259 else if ([_mode isEqualToString:NGFileReadWrite]) {
260 self->streamMode = NGStreamMode_readWrite;
261 openFlags = GENERIC_READ | GENERIC_WRITE;
262 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
265 [[[NGUnknownStreamModeException alloc]
266 initWithStream:self mode:_mode] raise];
270 self->fh = CreateFile([self->systemPath fileSystemRepresentation],
271 openFlags, shareMode, NULL,
272 OPEN_ALWAYS, // same as the Unix O_CREAT flag
273 0, // security flags ?
276 if (self->fh == INVALID_HANDLE_VALUE)
277 [NGCouldNotOpenStreamException raiseWithStream:self];
280 int openFlags; // flags passed to open() call
282 if (self->fd != NGInvalidUnixDescriptor)
283 [self close]; // if stream is open, close and reopen
285 if ([_mode isEqualToString:NGFileReadOnly]) {
286 self->streamMode = NGStreamMode_readOnly;
287 openFlags = O_RDONLY;
289 else if ([_mode isEqualToString:NGFileWriteOnly]) {
290 self->streamMode = NGStreamMode_writeOnly;
291 openFlags = O_WRONLY | O_CREAT;
293 else if ([_mode isEqualToString:NGFileReadWrite]) {
294 self->streamMode = NGStreamMode_readWrite;
295 openFlags = O_RDWR | O_CREAT;
297 else if ([_mode isEqualToString:NGFileAppend]) {
298 self->streamMode = NGStreamMode_writeOnly;
299 openFlags = O_WRONLY | O_CREAT | O_APPEND;
301 else if ([_mode isEqualToString:NGFileReadAppend]) {
302 self->streamMode = NGStreamMode_readWrite;
303 openFlags = O_RDWR | O_CREAT | O_APPEND;
306 [[[NGUnknownStreamModeException alloc]
307 initWithStream:self mode:_mode] raise];
311 self->fd = open([self->systemPath fileSystemRepresentation],
315 if (self->fd == -1) {
316 self->fd = NGInvalidUnixDescriptor;
318 [NGCouldNotOpenStreamException raiseWithStream:self];
323 self->markDelta = -1; // not marked
328 #if defined(__MINGW32__)
329 return (self->fh != INVALID_HANDLE_VALUE) ? YES : NO;
331 return (self->fd != NGInvalidUnixDescriptor) ? YES : NO;
335 // Foundation file handles
337 - (void)resetFileHandle { // called by NSFileHandle on dealloc
340 - (NSFileHandle *)fileHandle {
341 if (self->handle == nil)
342 self->handle = [[_NGConcreteFileStreamFileHandle allocWithZone:[self zone]]
343 initWithStream:self];
344 return [self->handle autorelease];
347 #if defined(__MINGW32__)
348 - (HANDLE)windowsFileHandle {
353 - (int)fileDescriptor {
354 #if defined(__MINGW32__)
355 return (int)[self fileHandle];
363 static void _checkOpen(NGFileStream *self, NSString *_reason) {
364 #if defined(__MINGW32__)
365 if (self->fh == INVALID_HANDLE_VALUE)
366 [NGStreamNotOpenException raiseWithStream:self reason:_reason];
368 if (self->fd == NGInvalidUnixDescriptor)
369 [NGStreamNotOpenException raiseWithStream:self reason:_reason];
373 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
375 // NGWriteOnlyStreamException when the stream is not readable
376 // NGStreamNotOpenException when the stream is not open
377 // NGEndOfStreamException when the end of the stream is reached
378 // NGStreamReadErrorException when the read call failed
380 _checkOpen(self, @"tried to read from a file stream which is closed");
382 if (!NGCanReadInStreamMode(streamMode))
383 [NGWriteOnlyStreamException raiseWithStream:self];
386 #if defined(__MINGW32__)
387 DWORD readResult = 0;
389 if (ReadFile(self->fh, _buf, _len, &readResult, NULL) == FALSE) {
390 DWORD lastErr = GetLastError();
392 if (lastErr == ERROR_HANDLE_EOF)
393 [NGEndOfStreamException raiseWithStream:self];
395 [NGStreamReadErrorException raiseWithStream:self errorCode:lastErr];
398 [NGEndOfStreamException raiseWithStream:self];
404 readResult = read(self->fd, _buf, _len);
407 [NGEndOfStreamException raiseWithStream:self];
408 else if (readResult == -1) {
411 if (errCode == EINTR)
412 // system call was interrupted
415 [NGStreamReadErrorException raiseWithStream:self errorCode:errCode];
418 while ((readResult <= 0) && (retryCount < 10));
420 if (retryCount >= 10)
421 [NGStreamReadErrorException raiseWithStream:self errorCode:EINTR];
424 NSAssert(readResult > 0, @"invalid read method state");
427 if (self->markDelta != -1)
428 self->markDelta += readResult; // increase delta
434 - (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len {
436 // NGReadOnlyStreamException when the stream is not writeable
437 // NGStreamNotOpenException when the stream is not open
438 // NGStreamWriteErrorException when the write call failed
440 _checkOpen(self, @"tried to write to a file stream which is closed");
442 if (!NGCanWriteInStreamMode(streamMode))
443 [NGReadOnlyStreamException raiseWithStream:self];
446 #if defined(__MINGW32__)
447 DWORD writeResult = 0;
449 if (WriteFile(self->fh, _buf, _len, &writeResult, NULL) == FALSE) {
450 DWORD errorCode = GetLastError();
453 case ERROR_INVALID_HANDLE:
454 [NGStreamWriteErrorException raiseWithStream:self
455 reason:@"incorrect file handle"];
457 case ERROR_WRITE_PROTECT:
458 [NGStreamWriteErrorException raiseWithStream:self
459 reason:@"disk write protected"];
461 case ERROR_NOT_READY:
462 [NGStreamWriteErrorException raiseWithStream:self
463 reason:@"the drive is not ready"];
465 case ERROR_HANDLE_EOF:
466 [NGStreamWriteErrorException raiseWithStream:self
467 reason:@"reached end of file"];
469 case ERROR_DISK_FULL:
470 [NGStreamWriteErrorException raiseWithStream:self
471 reason:@"disk is full"];
475 [NGStreamWriteErrorException raiseWithStream:self
476 errorCode:GetLastError()];
479 NSLog(@"invalid program state, aborting");
487 writeResult = write(self->fd, _buf, _len);
489 if (writeResult == -1) {
492 if (errCode == EINTR)
493 // system call was interrupted
496 [NGStreamWriteErrorException raiseWithStream:self errorCode:errno];
499 while ((writeResult == -1) && (retryCount < 10));
501 if (retryCount >= 10)
502 [NGStreamWriteErrorException raiseWithStream:self errorCode:EINTR];
510 #if defined(__MINGW32__)
511 if (self->fh == INVALID_HANDLE_VALUE) {
512 NSLog(@"tried to close already closed stream %@", self);
513 return YES; /* not signaled as an error .. */
516 if (CloseHandle(self->fh) == FALSE) {
517 [NGCouldNotCloseStreamException raiseWithStream:self];
521 self->fh = INVALID_HANDLE_VALUE;
523 if (self->fd == NGInvalidUnixDescriptor) {
524 NSLog(@"tried to close already closed stream %@", self);
525 return YES; /* not signaled as an error .. */
528 if (close(self->fd) != 0) {
529 [NGCouldNotCloseStreamException raiseWithStream:self];
533 self->fd = NGInvalidUnixDescriptor;
535 self->markDelta = -1;
539 - (NGStreamMode)mode {
540 return self->streamMode;
542 - (BOOL)isRootStream {
546 #if defined(__MINGW32__)
548 if (self->fh != INVALID_HANDLE_VALUE)
549 FlushFileBuffers(self->fh);
556 #if defined(__MINGW32__)
557 - (BOOL)wouldBlockInMode:(NGStreamMode)_mode {
558 NSLog(@"%@ not supported in Windows environment !",
559 NSStringFromSelector(_cmd));
563 - (BOOL)wouldBlockInMode:(NGStreamMode)_mode {
566 if (self->fd == NGInvalidUnixDescriptor)
569 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM;
570 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM;
572 // timeout of 0 means return immediatly
573 return (NGPollDescriptor(self->fd, events, 0) == 1 ? NO : YES);
584 if (![self moveByOffset:-(self->markDelta)])
586 self->markDelta = -1;
589 - (BOOL)markSupported {
593 // NGPositionableStream
595 - (BOOL)moveToLocation:(unsigned)_location {
596 self->markDelta = -1;
598 #if defined(__MINGW32__)
599 if (SetFilePointer(self->fh, _location, NULL, FILE_BEGIN) == -1) {
600 [NGStreamSeekErrorException raiseWithStream:self errorCode:GetLastError()];
604 if (lseek(self->fd, _location, SEEK_SET) == -1) {
605 [NGStreamSeekErrorException raiseWithStream:self errorCode:errno];
611 - (BOOL)moveByOffset:(int)_delta {
612 self->markDelta += _delta;
614 #if defined(__MINGW32__)
615 if (SetFilePointer(self->fh, _delta, NULL, FILE_CURRENT) == -1) {
616 [NGStreamSeekErrorException raiseWithStream:self errorCode:GetLastError()];
620 if (lseek(self->fd, _delta, SEEK_CUR) == -1) {
621 [NGStreamSeekErrorException raiseWithStream:self errorCode:errno];
630 - (NSString *)description {
631 return [NSString stringWithFormat:
632 @"<%@[0x%08X] path=%@ mode=%@>",
633 NSStringFromClass([self class]), (unsigned)self,
634 self->systemPath ? self->systemPath : @"nil",
635 [self modeDescription]];
640 @implementation _NGConcreteFileStreamFileHandle
644 #if defined(__MINGW32__)
645 - (HANDLE)fileHandle {
646 return [(NGFileStream *)stream windowsFileHandle];
650 - (int)fileDescriptor {
651 return [(NGFileStream *)stream fileDescriptor];