1/* 2 Copyright (C) 2000-2008 SKYRIX Software AG 3 Copyright (C) 2007-2008 Helge Hess 4 5 This file is part of SOPE. 6 7 SOPE is free software; you can redistribute it and/or modify it under 8 the terms of the GNU Lesser General Public License as published by the 9 Free Software Foundation; either version 2, or (at your option) any 10 later version. 11 12 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with SOPE; see the file COPYING. If not, write to the 19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 20 02111-1307, USA. 21*/ 22 23#include <NGStreams/NGCTextStream.h> 24#include <NGStreams/NGStreamExceptions.h> 25#include <NGStreams/NGFileStream.h> 26#include <NGExtensions/NGCharBuffers.h> 27#include "common.h" 28 29static NSString *NGNewLineString = @"\n"; 30 31@interface _NGCTextStreamLineEnumerator : NSEnumerator 32{ 33 NGCTextStream *stream; 34} 35- (id)initWithTextStream:(NGCTextStream *)_stream; 36- (id)nextObject; 37@end 38 39NGStreams_DECLARE id<NGExtendedTextInputStream> NGTextIn = nil; 40NGStreams_DECLARE id<NGExtendedTextOutputStream> NGTextOut = nil; 41NGStreams_DECLARE id<NGExtendedTextOutputStream> NGTextErr = nil; 42 43@implementation NGCTextStream 44 45// stdio 46 47NGStreams_DECLARE void NGInitTextStdio(void) { 48 static BOOL isInitialized = NO; 49 if (!isInitialized) { 50 isInitialized = YES; 51 52 NGInitStdio(); 53 54 NGTextIn = [(NGCTextStream *)[NGCTextStream alloc] 55 initWithSource:(id<NGStream>)NGIn]; 56 NGTextOut = [(NGCTextStream *)[NGCTextStream alloc] 57 initWithSource:(id<NGStream>)NGOut]; 58 NGTextErr = [(NGCTextStream *)[NGCTextStream alloc] 59 initWithSource:(id<NGStream>)NGErr]; 60 } 61} 62 63+ (void)_flushForExit:(NSNotification *)_notification { 64 // [NGTextIn flush]; 65 [NGTextIn release]; NGTextIn = nil; 66 [NGTextOut flush]; [NGTextOut release]; NGTextOut = nil; 67 [NGTextErr flush]; [NGTextErr release]; NGTextErr = nil; 68} 69 70static void _flushAtExit(void) { 71 // [NGTextIn flush]; 72 [NGTextIn release]; NGTextIn = nil; 73 [NGTextOut flush]; [NGTextOut release]; NGTextOut = nil; 74 [NGTextErr flush]; [NGTextErr release]; NGTextErr = nil; 75} 76 77+ (void)initialize { 78 BOOL isInitialized = NO; 79 if (!isInitialized) { 80 isInitialized = YES; 81 82 atexit(_flushAtExit); 83 } 84} 85 86// system text stream 87 88+ (id)textStreamWithInputSource:(id<NGInputStream>)_s { 89 if (_s == nil) return nil; 90 return [[(NGCTextStream *)[self alloc] initWithInputSource:_s] autorelease]; 91} 92+ (id)textStreamWithOutputSource:(id<NGOutputStream>)_s { 93 if (_s == nil) return nil; 94 return [[(NGCTextStream *)[self alloc] initWithOutputSource:_s] autorelease]; 95} 96+ (id)textStreamWithSource:(id<NGStream>)_stream { 97 if (_stream == nil) return nil; 98 return [[(NGCTextStream *)[self alloc] initWithSource:_stream] autorelease]; 99} 100 101- (id)initWithSource:(id<NGStream>)_stream { 102 if (_stream == nil) { 103 [self release]; 104 return nil; 105 } 106 if ((self = [super init]) != nil) { 107 self->source = [_stream retain]; 108 109 /* On MacOS 10.5 this is per default 30 aka MacOS Roman */ 110 self->encoding = [NSString defaultCStringEncoding]; 111 112#ifdef __APPLE__ 113 //# warning no selector caching on MacOSX ... 114#else 115 /* check whether we are dealing with a proxy .. */ 116 if ([source isKindOfClass:[NSObject class]]) { 117 self->readBytes = (NGIOReadMethodType) 118 [(NSObject *)source methodForSelector:@selector(readBytes:count:)]; 119 self->writeBytes = (NGIOWriteMethodType) 120 [(NSObject *)source methodForSelector:@selector(writeBytes:count:)]; 121 self->flushBuffer = (BOOL (*)(id,SEL)) 122 [(NSObject *)source methodForSelector:@selector(flush)]; 123 } 124#endif 125 } 126 return self; 127} 128- (id)initWithInputSource:(id<NGInputStream>)_source { 129 return [self initWithSource:(id)_source]; 130} 131- (id)initWithOutputSource:(id<NGOutputStream>)_source { 132 return [self initWithSource:(id)_source]; 133} 134 135- (void)dealloc { 136 [self->source release]; 137 self->readBytes = NULL; 138 self->writeBytes = NULL; 139 self->flushBuffer = NULL; 140 [super dealloc]; 141} 142 143/* accessors */ 144 145- (id<NGStream>)source { 146 return self->source; 147} 148- (int)fileDescriptor { 149 return [(id)[self source] fileDescriptor]; 150} 151 152- (BOOL)isOpen { 153 return [(id)[self source] isOpen]; 154} 155 156/* operations */ 157 158- (BOOL)close { 159 return [self->source close]; 160} 161 162/* NGTextInputStream */ 163 164- (unichar)readCharacter { 165 return [self readChar]; 166} 167 168- (unsigned char)readChar { 169 unsigned char c; 170 unsigned res; 171 172 if (readBytes) { 173 res = readBytes(self->source, @selector(readBytes:count:), 174 &c, sizeof(unsigned char)); 175 } 176 else 177 res = [self->source readBytes:&c count:sizeof(unsigned char)]; 178 179 if (res == NGStreamError) { 180 [self setLastException:[self->source lastException]]; 181 return -1; 182 } 183 184 return c; 185} 186 187/* TODO: fix exception handling */ 188 189- (NSString *)readLineAsString { 190 NGCharBuffer8 buffer = NULL; 191 unsigned char c; 192 193 *(&buffer) = NGCharBuffer8_new(128); 194 195 NS_DURING { 196 unsigned int res; 197 198 if (readBytes) { 199 do { 200 res = self->readBytes(source, @selector(readBytes:count:), 201 &c, sizeof(unsigned char)); 202 if (res != 1) [[self->source lastException] raise]; 203 204 if (c == '\r') { 205 res = readBytes(source, @selector(readBytes:count:), 206 &c, sizeof(unsigned char)); 207 if (res != 1) [[self->source lastException] raise]; 208 } 209 210 if ((c != '\n') && (c != 0)) { 211 NSAssert1(c != 0, @"tried to add '0' character to buffer '%s' ..", 212 buffer->buffer); 213 NGCharBuffer8_addChar(buffer, c); 214 } 215 } 216 while ((c != '\n') && (c != 0)); 217 } 218 else { 219 do { 220 res = [self->source readBytes:&c count:sizeof(unsigned char)]; 221 /* TODO: raises exception */ 222 if (res != 1) [[self->source lastException] raise]; 223 if (c == '\r') { 224 res = [self->source readBytes:&c count:sizeof(unsigned char)]; 225 if (res != 1) [[self->source lastException] raise]; 226 } 227 228 if ((c != '\n') && (c != 0)) 229 NGCharBuffer8_addChar(buffer, c); 230 } 231 while ((c != '\n') && (c != 0)); 232 } 233 } 234 NS_HANDLER { 235 if ([localException isKindOfClass:[NGEndOfStreamException class]]) { 236 if (buffer->length == 0) { 237 NGCharBuffer8_dealloc(buffer); 238 buffer = NULL; 239 } 240 } 241 else 242 [localException raise]; 243 } 244 NS_ENDHANDLER; 245 246 return buffer ? NGCharBuffer8_makeStringAndDealloc(buffer) : (NSString *)nil; 247} 248 249- (NSEnumerator *)lineEnumerator { 250 return [[[_NGCTextStreamLineEnumerator alloc] 251 initWithTextStream:self] 252 autorelease]; 253} 254 255 256/* NGTextOutputStream */ 257 258- (BOOL)writeCharacter:(unichar)_character { 259 unsigned char c; 260 unsigned res; 261 262 if (_character > ((sizeof(unsigned char) * 256) - 1)) { 263 // character is not in range of maximum system encoding 264 [NSException raise:@"NGCTextStreamEncodingException" 265 format: 266 @"called writeCharacter: with character code (0x%X)" 267 @" exceeding the maximum system character code (0x%X)", 268 _character, (int)((sizeof(unsigned char) * 256) - 1)]; 269 } 270 271 c = _character; 272 273 if (self->writeBytes != NULL) { 274 res = self->writeBytes(self->source, @selector(writeBytes:count:), 275 &c, sizeof(unsigned char)); 276 } 277 else 278 res = [self->source writeBytes:&c count:sizeof(unsigned char)]; 279 280 if (res == NGStreamError) { 281 [self setLastException:[self->source lastException]]; 282 return NO; 283 } 284 285 return YES; 286} 287 288- (BOOL)writeString:(NSString *)_string { 289 unsigned char *str, *buf; 290 unsigned toGo; 291 292#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1040 || (GNUSTEP && OS_API_VERSION(100400,GS_API_LATEST)) 293 NSData *d; 294 295 if ((toGo = [_string lengthOfBytesUsingEncoding:self->encoding]) == 0) 296 return YES; 297 298 d = [_string dataUsingEncoding: self->encoding 299 allowLossyConversion: NO]; 300 301 if (!d) { 302 NSLog(@"ERROR(%s): failed to extract data using encoding(%i)" 303 @" from NSString: '%@'\n", __PRETTY_FUNCTION__, 304 self->encoding, _string); 305 return NO; 306 } 307 308 buf = str = calloc(toGo + 1, sizeof(unsigned char)); 309 [d getBytes: str length: toGo]; 310 str[toGo] = '\0'; 311#else 312 if ((toGo = [_string cStringLength]) == 0) 313 return YES; 314 315 buf = str = calloc(toGo + 2, sizeof(unsigned char)); 316 [_string getCString:(char *)str]; 317 str[toGo] = '\0'; 318#endif 319 320 NS_DURING { 321 while (toGo > 0) { 322 unsigned writeCount; 323 324 writeCount = writeBytes 325 ? writeBytes(source, @selector(writeBytes:count:), str, toGo) 326 : [source writeBytes:str count:toGo]; 327 328 if (writeCount == NGStreamError) 329 [[self->source lastException] raise]; 330 331 toGo -= writeCount; 332 str += writeCount; 333 } 334 } 335 NS_HANDLER { 336 if (buf != NULL) { free(buf); buf = NULL; }; 337 [localException raise]; 338 } 339 NS_ENDHANDLER; 340 341 if (buf) { free(buf); buf = NULL; } 342 return YES; 343} 344 345- (BOOL)flush { 346 if (flushBuffer) 347 return flushBuffer(self->source, @selector(flush)); 348 else 349 return [self->source flush]; 350} 351 352- (BOOL)writeNewline { 353 if (![self writeString:NGNewLineString]) return NO; 354 if (![self flush]) return NO; 355 return YES; 356} 357 358- (void) setEncoding: (NSStringEncoding) theEncoding 359{ 360 self->encoding = theEncoding; 361} 362 363@end /* NGCTextStream */ 364 365@implementation _NGCTextStreamLineEnumerator 366 367- (id)initWithTextStream:(NGCTextStream *)_stream { 368 self->stream = [_stream retain]; 369 return self; 370} 371 372- (void)dealloc { 373 [self->stream release]; 374 [super dealloc]; 375} 376 377- (id)nextObject { 378 id result; 379 380 *(&result) = nil; 381 382 NS_DURING { 383 result = [self->stream readLineAsString]; 384 } 385 NS_HANDLER { 386 if ([localException isKindOfClass:[NGEndOfStreamException class]]) 387 result = nil; 388 else 389 [localException raise]; 390 } 391 NS_ENDHANDLER; 392 393 return result; 394} 395 396@end /* _NGCTextStreamLineEnumerator */ 397