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