]> err.no Git - sope/blob - sope-core/NGStreams/NGCTextStream.m
fixed OSX installdir
[sope] / sope-core / NGStreams / NGCTextStream.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include <NGStreams/NGCTextStream.h>
23 #include <NGStreams/NGStreamExceptions.h>
24 #include <NGStreams/NGFileStream.h>
25 #include <NGExtensions/NGCharBuffers.h>
26 #include "common.h"
27
28 static NSString *NGNewLineString = @"\n";
29
30 @interface _NGCTextStreamLineEnumerator : NSEnumerator
31 {
32   NGCTextStream *stream;
33 }
34 - (id)initWithTextStream:(NGCTextStream *)_stream;
35 - (id)nextObject;
36 @end
37
38 NGStreams_DECLARE id<NGExtendedTextInputStream>  NGTextIn  = nil;
39 NGStreams_DECLARE id<NGExtendedTextOutputStream> NGTextOut = nil;
40 NGStreams_DECLARE id<NGExtendedTextOutputStream> NGTextErr = nil;
41
42 @implementation NGCTextStream
43
44 + (int)version {
45   return [super version] + 0 /* v2 */;
46 }
47
48 // stdio
49
50 NGStreams_DECLARE void NGInitTextStdio(void) {
51   static BOOL isInitialized = NO;
52   if (!isInitialized) {
53     isInitialized = YES;
54     
55     NGInitStdio();
56     
57     NGTextIn  = [(NGCTextStream *)[NGCTextStream alloc]
58                     initWithSource:(id<NGStream>)NGIn];
59     NGTextOut = [(NGCTextStream *)[NGCTextStream alloc]
60                     initWithSource:(id<NGStream>)NGOut];
61     NGTextErr = [(NGCTextStream *)[NGCTextStream alloc]
62                     initWithSource:(id<NGStream>)NGErr];
63   }
64 }
65
66 + (void)_flushForExit:(NSNotification *)_notification {
67   // [NGTextIn  flush]; 
68   [NGTextIn  release]; NGTextIn  = nil;
69   [NGTextOut flush]; [NGTextOut release]; NGTextOut = nil;
70   [NGTextErr flush]; [NGTextErr release]; NGTextErr = nil;
71 }
72
73 static void _flushAtExit(void) {
74   // [NGTextIn flush];
75   [NGTextIn  release]; NGTextIn  = nil;
76   [NGTextOut flush]; [NGTextOut release]; NGTextOut = nil;
77   [NGTextErr flush]; [NGTextErr release]; NGTextErr = nil;
78 }
79
80 + (void)initialize {
81   BOOL isInitialized = NO;
82   if (!isInitialized) {
83     NSAssert2([super version] == 2,
84               @"invalid superclass (%@) version %i !",
85               NSStringFromClass([self superclass]), [super version]);
86     isInitialized = YES;
87
88     atexit(_flushAtExit);
89   }
90 }
91
92 // system text stream
93
94 + (id)textStreamWithInputSource:(id<NGInputStream>)_s {
95   if (_s == nil) return nil;
96   return [[(NGCTextStream *)[self alloc] initWithInputSource:_s] autorelease];
97 }
98 + (id)textStreamWithOutputSource:(id<NGOutputStream>)_s {
99   if (_s == nil) return nil;
100   return [[(NGCTextStream *)[self alloc] initWithOutputSource:_s] autorelease];
101 }
102 + (id)textStreamWithSource:(id<NGStream>)_stream {
103   if (_stream == nil) return nil;
104   return [[(NGCTextStream *)[self alloc] initWithSource:_stream] autorelease];
105 }
106
107 - (id)initWithSource:(id<NGStream>)_stream {
108   if (_stream == nil) {
109     [self release];
110     return nil;
111   }
112   if ((self = [super init])) {
113     self->source = [_stream retain];
114
115 #ifdef __APPLE__
116     //#  warning no selector caching on MacOSX ...
117 #else
118     /* check whether we are dealing with a proxy .. */
119     if ([source isKindOfClass:[NSObject class]]) {
120       self->readBytes   = (NGIOReadMethodType)
121         [(NSObject *)source methodForSelector:@selector(readBytes:count:)];
122       self->writeBytes  = (NGIOWriteMethodType)
123         [(NSObject *)source methodForSelector:@selector(writeBytes:count:)];
124       self->flushBuffer = (BOOL (*)(id,SEL))
125         [(NSObject *)source methodForSelector:@selector(flush)];
126     }
127 #endif
128   }
129   return self;
130 }
131 - (id)initWithInputSource:(id<NGInputStream>)_source {
132   return [self initWithSource:(id)_source];
133 }
134 - (id)initWithOutputSource:(id<NGOutputStream>)_source {
135   return [self initWithSource:(id)_source];
136 }
137
138 - (void)dealloc {
139   [self->source release];
140   self->readBytes   = NULL;
141   self->writeBytes  = NULL;
142   self->flushBuffer = NULL;
143   [super dealloc];
144 }
145
146 /* accessors */
147
148 - (id<NGStream>)source {
149   return self->source;
150 }
151 - (int)fileDescriptor {
152   return [(id)[self source] fileDescriptor];
153 }
154
155 - (BOOL)isOpen {
156   return [(id)[self source] isOpen];
157 }
158
159 /* operations */
160
161 - (BOOL)close {
162   return [self->source close];
163 }
164
165 /* NGTextInputStream */
166
167 - (unichar)readCharacter {
168   return [self readChar];
169 }
170
171 - (unsigned char)readChar {
172   unsigned char c;
173   unsigned res;
174   
175   if (readBytes) {
176     res = readBytes(self->source, @selector(readBytes:count:),
177                     &c, sizeof(unsigned char));
178   }
179   else
180     res = [self->source readBytes:&c count:sizeof(unsigned char)];
181   
182   if (res == NGStreamError) {
183     [self setLastException:[self->source lastException]];
184     return -1;
185   }
186   
187   return c;
188 }
189
190 /* TODO: fix exception handling */
191
192 - (NSString *)readLineAsString {
193   NGCharBuffer8   buffer = NULL;
194   unsigned char   c;
195
196   *(&buffer) = NGCharBuffer8_new(128);
197
198   NS_DURING {
199     unsigned int res;
200     
201     if (readBytes) {
202       do {
203         res = self->readBytes(source, @selector(readBytes:count:),
204                         &c, sizeof(unsigned char));
205         if (res != 1) [[self->source lastException] raise];
206         
207         if (c == '\r') {
208           res = readBytes(source, @selector(readBytes:count:),
209                           &c, sizeof(unsigned char));
210           if (res != 1) [[self->source lastException] raise];
211         }
212         
213         if ((c != '\n') && (c != 0)) {
214           NSAssert1(c != 0, @"tried to add '0' character to buffer '%s' ..",
215                     buffer->buffer);
216           NGCharBuffer8_addChar(buffer, c);
217         }
218       }
219       while ((c != '\n') && (c != 0));
220     }
221     else {
222       do {
223         res = [self->source readBytes:&c count:sizeof(unsigned char)];
224         /* TODO: raises exception */
225         if (res != 1) [[self->source lastException] raise];
226         if (c == '\r') {
227           res = [self->source readBytes:&c count:sizeof(unsigned char)];
228           if (res != 1) [[self->source lastException] raise];
229         }
230         
231         if ((c != '\n') && (c != 0))
232           NGCharBuffer8_addChar(buffer, c);
233       }
234       while ((c != '\n') && (c != 0));
235     }
236   }
237   NS_HANDLER {
238     if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
239       if (buffer->length == 0) {
240         NGCharBuffer8_dealloc(buffer);
241         buffer = NULL;
242       }
243     }
244     else
245       [localException raise];
246   }
247   NS_ENDHANDLER;
248   
249   return buffer ? NGCharBuffer8_makeStringAndDealloc(buffer) : nil;
250 }
251
252 - (NSEnumerator *)lineEnumerator {
253   return [[[_NGCTextStreamLineEnumerator alloc]
254                                          initWithTextStream:self]
255                                          autorelease];
256 }
257
258 // NGTextOutputStream
259
260 - (BOOL)writeCharacter:(unichar)_character {
261   unsigned char c;
262   unsigned res;
263   
264   if (_character > ((sizeof(unsigned char) * 256) - 1)) {
265     // character is not in range of maximum system encoding
266     [NSException raise:@"NGCTextStreamEncodingException"
267                  format:
268                    @"called writeCharacter: with character code (0x%X) exceeding"
269                    @" the maximum system character code (0x%X)",
270                    _character, ((sizeof(unsigned char) * 256) - 1)];
271   }
272
273   c = _character;
274
275   if (writeBytes) {
276     res = writeBytes(self->source, @selector(writeBytes:count:),
277                      &c, sizeof(unsigned char));
278   }
279   else
280     res = [self->source writeBytes:&c count:sizeof(unsigned char)];
281
282   if (res == NGStreamError) {
283     [self setLastException:[self->source lastException]];
284     return NO;
285   }
286   
287   return YES;
288 }
289
290 - (BOOL)writeString:(NSString *)_string {
291   unsigned char *str, *buf;
292   unsigned toGo;
293   
294   if ((toGo = [_string cStringLength]) == 0)
295     return YES;
296   
297   buf = str = calloc(toGo + 1, sizeof(unsigned char));
298   [_string getCString:(char *)str]; str[toGo] = '\0';
299   
300   NS_DURING {
301     while (toGo > 0) {
302       unsigned writeCount;
303       
304       writeCount = writeBytes
305         ? writeBytes(source, @selector(writeBytes:count:), str, toGo)
306         : [source writeBytes:str count:toGo];
307       
308       if (writeCount == NGStreamError)
309         [[self->source lastException] raise];
310       
311       toGo -= writeCount;
312       str  += writeCount;
313     }
314   }
315   NS_HANDLER {
316     if (buf) { free(buf); buf = NULL; };
317     [localException raise];
318   }
319   NS_ENDHANDLER;
320   
321   if (buf) { free(buf); buf = NULL; }
322   return YES;
323 }
324
325 - (BOOL)flush {
326   if (flushBuffer)
327     return flushBuffer(self->source, @selector(flush));
328   else
329     return [self->source flush];
330 }
331
332 - (BOOL)writeNewline {
333   if (![self writeString:NGNewLineString]) return NO;
334   if (![self flush]) return NO;
335   return YES;
336 }
337
338 @end /* NGCTextStream */
339
340 @implementation _NGCTextStreamLineEnumerator
341
342 - (id)initWithTextStream:(NGCTextStream *)_stream {
343   self->stream = [_stream retain];
344   return self;
345 }
346
347 - (void)dealloc {
348   [self->stream release];
349   [super dealloc];
350 }
351
352 - (id)nextObject {
353   id result;
354
355   *(&result) = nil;
356   
357   NS_DURING {
358     result = [self->stream readLineAsString];
359   }
360   NS_HANDLER {
361     if ([localException isKindOfClass:[NGEndOfStreamException class]])
362       result = nil;
363     else
364       [localException raise];
365   }
366   NS_ENDHANDLER;
367   
368   return result;
369 }
370
371 @end /* _NGCTextStreamLineEnumerator */