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]), 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];