|
1 " |
|
2 COPYRIGHT (c) 1988-93 by Claus Gittinger |
|
3 All Rights Reserved |
|
4 |
|
5 This software is furnished under a license and may be used |
|
6 only in accordance with the terms of that license and with the |
|
7 inclusion of the above copyright notice. This software may not |
|
8 be provided or otherwise made available to, or used by, any |
|
9 other person. No title to or ownership of the software is |
|
10 hereby transferred. |
|
11 " |
|
12 |
|
13 ReadWriteStream subclass:#ExternalStream |
|
14 instanceVariableNames:'filePointer mode unBuffered binary' |
|
15 classVariableNames:'lobby' |
|
16 poolDictionaries:'' |
|
17 category:'Streams-External' |
|
18 ! |
|
19 |
|
20 ExternalStream comment:' |
|
21 |
|
22 COPYRIGHT (c) 1988-93 by Claus Gittinger |
|
23 All Rights Reserved |
|
24 |
|
25 ExternalStream defines protocol common to Streams which have a file-descriptor and |
|
26 represent some File or CommunicationChannel of the underlying OperatingSystem. |
|
27 ExternalStream is abstract; concrete classes are FileStream, PipeStream etc. |
|
28 ExternalStreams can be in two modes: text (the default) and binary. |
|
29 In text-mode, the elements read/written are characters; while in binary-mode the basic |
|
30 elements are bytes which read/write as SmallIntegers in the range 0..255. |
|
31 |
|
32 %W% %E% |
|
33 |
|
34 written 88 by claus |
|
35 '! |
|
36 |
|
37 %{ |
|
38 #include <stdio.h> |
|
39 %} |
|
40 |
|
41 !ExternalStream class methodsFor:'initialization'! |
|
42 |
|
43 initialize |
|
44 lobby isNil ifTrue:[ |
|
45 lobby := Registry new. |
|
46 |
|
47 "want to get informed when returning from snapshot" |
|
48 ObjectMemory addDependent:self |
|
49 ] |
|
50 ! |
|
51 |
|
52 reOpenFiles |
|
53 "reopen all files (if possible) after a snapShot load" |
|
54 |
|
55 lobby contentsDo:[:aFileStream | |
|
56 aFileStream reOpen |
|
57 ] |
|
58 ! |
|
59 |
|
60 update:something |
|
61 "have to reopen files when returning from snapshot" |
|
62 |
|
63 something == #returnFromSnapshot ifTrue:[ |
|
64 self reOpenFiles |
|
65 ] |
|
66 ! ! |
|
67 |
|
68 !ExternalStream methodsFor:'instance release'! |
|
69 |
|
70 disposed |
|
71 "some Stream has been collected - close the file if not already done" |
|
72 |
|
73 self closeFile |
|
74 ! |
|
75 |
|
76 closeFile |
|
77 "low level close - may be redefined in subclasses" |
|
78 |
|
79 %{ /* NOCONTEXT */ |
|
80 |
|
81 fclose(MKFD(_INST(filePointer))); |
|
82 %} |
|
83 ! ! |
|
84 |
|
85 !ExternalStream methodsFor:'private'! |
|
86 |
|
87 reOpen |
|
88 "sent after snapin to reopen streams. |
|
89 cannot reopen here since I am abstract and have no device knowledge" |
|
90 |
|
91 self class name print. ': cannot reOpen stream - stream closed' printNewline. |
|
92 filePointer := nil. |
|
93 lobby unregister:self. |
|
94 ! ! |
|
95 |
|
96 !ExternalStream methodsFor:'error handling'! |
|
97 |
|
98 errorNotOpen |
|
99 "report an error, that the stream has not been opened" |
|
100 |
|
101 ^ self error:(self class name , ' not open') |
|
102 ! |
|
103 |
|
104 errorReadOnly |
|
105 "report an error, that the stream is a readOnly stream" |
|
106 |
|
107 ^ self error:(self class name , ' is readonly') |
|
108 ! |
|
109 |
|
110 errorWriteOnly |
|
111 "report an error, that the stream is a writeOnly stream" |
|
112 |
|
113 ^ self error:(self class name , ' is writeonly') |
|
114 ! |
|
115 |
|
116 errorNotBinary |
|
117 "report an error, that the stream is not in binary mode" |
|
118 |
|
119 ^ self error:(self class name , ' is not in binary mode') |
|
120 ! |
|
121 |
|
122 argumentMustBeInteger |
|
123 "report an error, that the argument must be an integer" |
|
124 |
|
125 ^ self error:'argument must be an integer' |
|
126 ! |
|
127 |
|
128 argumentMustBeCharacter |
|
129 "report an error, that the argument must be a character" |
|
130 |
|
131 ^ self error:'argument must be a character' |
|
132 ! |
|
133 |
|
134 argumentMustBeString |
|
135 "report an error, that the argument must be a string" |
|
136 |
|
137 ^ self error:'argument must be a string' |
|
138 ! ! |
|
139 |
|
140 !ExternalStream methodsFor:'accessing'! |
|
141 |
|
142 readonly |
|
143 "set access mode to readonly" |
|
144 |
|
145 mode := #readonly |
|
146 ! |
|
147 |
|
148 writeonly |
|
149 "set access mode to writeonly" |
|
150 |
|
151 mode := #writeonly |
|
152 ! |
|
153 |
|
154 readwrite |
|
155 "set access mode to readwrite" |
|
156 |
|
157 mode := #readwrite |
|
158 ! |
|
159 |
|
160 filePointer |
|
161 "return the filePointer of the receiver - |
|
162 notice: for portability stdio is used; this means you will get |
|
163 a FILE * - not a fileDescriptor" |
|
164 |
|
165 ^ filePointer |
|
166 ! |
|
167 |
|
168 fileDescriptor |
|
169 "return the fileDescriptor of the receiver - |
|
170 notice: this one returns the underlying OSs fileDescriptor - |
|
171 this may not be available on all platforms (i.e. non unix systems)." |
|
172 |
|
173 %{ /* NOCONTEXT */ |
|
174 |
|
175 FILE *f; |
|
176 |
|
177 if (_INST(filePointer) != nil) { |
|
178 f = MKFD(_INST(filePointer)); |
|
179 RETURN ( MKOBJ(fileno(f)) ); |
|
180 } |
|
181 %} |
|
182 . |
|
183 ^ self errorNotOpen |
|
184 ! |
|
185 |
|
186 buffered:aBoolean |
|
187 "turn buffering on or off" |
|
188 |
|
189 unBuffered := aBoolean not |
|
190 ! |
|
191 |
|
192 binary |
|
193 "switch to binary mode" |
|
194 |
|
195 binary := true |
|
196 ! |
|
197 |
|
198 text |
|
199 "switch to text mode" |
|
200 |
|
201 binary := false |
|
202 ! |
|
203 |
|
204 contents |
|
205 "return the contents of the file as a Text-object" |
|
206 |
|
207 |text| |
|
208 |
|
209 text := Text new. |
|
210 [self atEnd] whileFalse:[ |
|
211 text add:(self nextLine) |
|
212 ]. |
|
213 ^ text |
|
214 ! ! |
|
215 |
|
216 !ExternalStream methodsFor:'basic'! |
|
217 |
|
218 open |
|
219 "open the stream |
|
220 - this must be redefined in subclass" |
|
221 |
|
222 ^ self subclassResponsibility |
|
223 ! |
|
224 |
|
225 create |
|
226 "create the stream |
|
227 - this must be redefined in subclass" |
|
228 |
|
229 ^ self subclassResponsibility |
|
230 ! |
|
231 |
|
232 position |
|
233 "return the position |
|
234 - this must be redefined in subclass" |
|
235 |
|
236 ^ self subclassResponsibility |
|
237 ! |
|
238 |
|
239 position:anInteger |
|
240 "set the position |
|
241 - this must be redefined in subclass" |
|
242 |
|
243 ^ self subclassResponsibility |
|
244 ! ! |
|
245 |
|
246 !ExternalStream methodsFor:'low level I/O'! |
|
247 |
|
248 ioctl:ioctlNumber with:arg |
|
249 "to provide a simple ioctl facility" |
|
250 |
|
251 %{ /* NOCONTEXT */ |
|
252 |
|
253 FILE *f; |
|
254 int ret, ioNum, ioArg; |
|
255 extern OBJ ErrorNumber; |
|
256 extern errno; |
|
257 |
|
258 if (_INST(filePointer) != nil) { |
|
259 if (_isSmallInteger(ioctlNumber) && _isSmallInteger(arg)) { |
|
260 ioNum = _intVal(ioctlNumber); |
|
261 ioArg = _intVal(arg); |
|
262 f = MKFD(_INST(filePointer)); |
|
263 ret = ioctl(fileno(f), ioNum, ioArg); |
|
264 if (ret >= 0) { |
|
265 RETURN ( _MKSMALLINT(ret) ); |
|
266 } |
|
267 ErrorNumber = _MKSMALLINT(errno); |
|
268 RETURN ( nil ); |
|
269 } |
|
270 } |
|
271 %} |
|
272 . |
|
273 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
274 self primitiveFailed |
|
275 ! |
|
276 |
|
277 nextByte |
|
278 "read the next byte return it as an Integer |
|
279 nil on error. Use with care - non object oriented i/o" |
|
280 |
|
281 %{ /* NOCONTEXT */ |
|
282 |
|
283 FILE *f; |
|
284 unsigned char byte; |
|
285 int cnt; |
|
286 extern OBJ ErrorNumber; |
|
287 extern errno; |
|
288 extern int _immediateInterrupt; |
|
289 |
|
290 if (_INST(filePointer) != nil) { |
|
291 if (_INST(mode) != _writeonly) { |
|
292 f = MKFD(_INST(filePointer)); |
|
293 _immediateInterrupt = 1; |
|
294 if (_INST(unBuffered) == true) { |
|
295 cnt = read(fileno(f), &byte, 1); |
|
296 } else { |
|
297 if (_INST(mode) == _readwrite) |
|
298 fseek(f, 0L, 1); /* needed in stdio */ |
|
299 cnt = fread(&byte, 1, 1, f); |
|
300 } |
|
301 _immediateInterrupt = 0; |
|
302 if (cnt == 1) { |
|
303 if (_INST(position) != nil) |
|
304 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1); |
|
305 RETURN ( _MKSMALLINT(byte) ); |
|
306 } |
|
307 if (cnt < 0) { |
|
308 ErrorNumber = _MKSMALLINT(errno); |
|
309 } |
|
310 RETURN ( nil ); |
|
311 } |
|
312 } |
|
313 %} |
|
314 . |
|
315 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
316 self errorWriteOnly |
|
317 ! |
|
318 |
|
319 nextBytes:count into:anObject |
|
320 "read the next count bytes into an object and return the number of |
|
321 bytes read or nil on error. |
|
322 Use with care - non object oriented i/o" |
|
323 |
|
324 ^ self nextBytes:count into:anObject startingAt:1 |
|
325 ! |
|
326 |
|
327 nextBytes:count into:anObject startingAt:start |
|
328 "read the next count bytes into an object and return the number of |
|
329 bytes read or nil on error. |
|
330 Use with care - non object oriented i/o" |
|
331 |
|
332 %{ /* NOCONTEXT */ |
|
333 |
|
334 FILE *f; |
|
335 int cnt, offs; |
|
336 int objSize; |
|
337 char *cp; |
|
338 extern OBJ ErrorNumber; |
|
339 extern errno; |
|
340 OBJ pos; |
|
341 extern int _immediateInterrupt; |
|
342 |
|
343 if (_INST(filePointer) != nil) { |
|
344 if (_INST(mode) != _writeonly) { |
|
345 if (_isSmallInteger(count) && _isSmallInteger(start)) { |
|
346 cnt = _intVal(count); |
|
347 offs = _intVal(start) - 1; |
|
348 f = MKFD(_INST(filePointer)); |
|
349 objSize = _Size(anObject) - OHDR_SIZE; |
|
350 if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) { |
|
351 cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs; |
|
352 _immediateInterrupt = 1; |
|
353 if (_INST(unBuffered) == true) { |
|
354 cnt = read(fileno(f), cp, cnt); |
|
355 } else { |
|
356 if (_INST(mode) == _readwrite) |
|
357 fseek(f, 0L, 1); /* needed in stdio */ |
|
358 cnt = fread(cp, 1, cnt, f); |
|
359 } |
|
360 _immediateInterrupt = 0; |
|
361 if (cnt >= 0) { |
|
362 pos = _INST(position); |
|
363 if (pos != nil) |
|
364 _INST(position) = _MKSMALLINT(_intVal(pos) + cnt); |
|
365 RETURN ( _MKSMALLINT(cnt) ); |
|
366 } |
|
367 ErrorNumber = _MKSMALLINT(errno); |
|
368 RETURN ( nil ); |
|
369 } |
|
370 } |
|
371 } |
|
372 } |
|
373 %} |
|
374 . |
|
375 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
376 (mode == #writeonly) ifTrue:[^ self errorWriteOnly]. |
|
377 self primitiveFailed |
|
378 ! |
|
379 |
|
380 nextPutByte:aByteValue |
|
381 "write a byte" |
|
382 |
|
383 %{ /* NOCONTEXT */ |
|
384 |
|
385 FILE *f; |
|
386 char c; |
|
387 extern OBJ ErrorNumber; |
|
388 extern errno; |
|
389 OBJ pos; |
|
390 int cnt; |
|
391 extern int _immediateInterrupt; |
|
392 |
|
393 if (_INST(filePointer) != nil) { |
|
394 if (_INST(mode) != _readonly) { |
|
395 if (_isSmallInteger(aByteValue)) { |
|
396 c = _intVal(aByteValue); |
|
397 f = MKFD(_INST(filePointer)); |
|
398 _immediateInterrupt = 1; |
|
399 if (_INST(mode) == _readwrite) |
|
400 fseek(f, 0L, 1); /* needed in stdio */ |
|
401 cnt = fwrite(&c, 1, 1, f); |
|
402 _immediateInterrupt = 0; |
|
403 if (cnt == 1) { |
|
404 if (_INST(unBuffered) == true) { |
|
405 fflush(f); |
|
406 } |
|
407 pos = _INST(position); |
|
408 if (pos != nil) |
|
409 _INST(position) = _MKSMALLINT(_intVal(pos) + 1); |
|
410 RETURN ( self ); |
|
411 } |
|
412 ErrorNumber = _MKSMALLINT(errno); |
|
413 } |
|
414 } |
|
415 } |
|
416 %} |
|
417 . |
|
418 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
419 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
420 self primitiveFailed |
|
421 ! |
|
422 |
|
423 nextPutBytes:count from:anObject |
|
424 "write count bytes from an object starting at index start. |
|
425 return the number of bytes written or nil on error. |
|
426 Use with care - non object oriented i/o" |
|
427 |
|
428 ^ self nextPutBytes:count from:anObject startingAt:1 |
|
429 ! |
|
430 |
|
431 nextPutBytes:count from:anObject startingAt:start |
|
432 "write count bytes from an object starting at index start. |
|
433 return the number of bytes written or nil on error. |
|
434 Use with care - non object oriented i/o" |
|
435 |
|
436 %{ /* NOCONTEXT */ |
|
437 |
|
438 FILE *f; |
|
439 int cnt, offs; |
|
440 int objSize; |
|
441 char *cp; |
|
442 extern OBJ ErrorNumber; |
|
443 extern errno; |
|
444 OBJ pos; |
|
445 extern int _immediateInterrupt; |
|
446 |
|
447 if (_INST(filePointer) != nil) { |
|
448 if (_INST(mode) != _readonly) { |
|
449 if (_isSmallInteger(count) && _isSmallInteger(start)) { |
|
450 cnt = _intVal(count); |
|
451 offs = _intVal(start) - 1; |
|
452 f = MKFD(_INST(filePointer)); |
|
453 |
|
454 objSize = _Size(anObject) - OHDR_SIZE; |
|
455 if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) { |
|
456 cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs; |
|
457 _immediateInterrupt = 1; |
|
458 if (_INST(unBuffered) == true) { |
|
459 cnt = write(fileno(f), cp, cnt); |
|
460 } else { |
|
461 if (_INST(mode) == _readwrite) |
|
462 fseek(f, 0L, 1); /* needed in stdio */ |
|
463 cnt = fwrite(cp, 1, cnt, f); |
|
464 } |
|
465 _immediateInterrupt = 0; |
|
466 if (cnt >= 0) { |
|
467 pos = _INST(position); |
|
468 if (pos != nil) |
|
469 _INST(position) = _MKSMALLINT(_intVal(pos) + cnt); |
|
470 RETURN ( _MKSMALLINT(cnt) ); |
|
471 } |
|
472 ErrorNumber = _MKSMALLINT(errno); |
|
473 RETURN ( nil ); |
|
474 } |
|
475 } |
|
476 } |
|
477 } |
|
478 %} |
|
479 . |
|
480 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
481 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
482 self primitiveFailed |
|
483 ! ! |
|
484 |
|
485 !ExternalStream methodsFor:'character I/O'! |
|
486 |
|
487 peek |
|
488 "return the character to be read next without advancing read position" |
|
489 |
|
490 %{ /* NOCONTEXT */ |
|
491 |
|
492 FILE *f; |
|
493 REGISTER int c; |
|
494 extern int _immediateInterrupt; |
|
495 |
|
496 if (_INST(filePointer) != nil) { |
|
497 if (_INST(mode) != _writeonly) { |
|
498 f = MKFD(_INST(filePointer)); |
|
499 c = getc(f); |
|
500 if (c != EOF) { |
|
501 ungetc(c, f); |
|
502 if (_INST(binary) == true) { |
|
503 RETURN ( _MKSMALLINT(c & 0xFF) ); |
|
504 } |
|
505 RETURN ( _MKCHARACTER(c & 0xFF) ); |
|
506 } |
|
507 RETURN ( nil ); |
|
508 } |
|
509 } |
|
510 %} |
|
511 . |
|
512 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
513 self errorWriteOnly |
|
514 ! |
|
515 |
|
516 next |
|
517 "return the character; advance read position" |
|
518 |
|
519 %{ /* NOCONTEXT */ |
|
520 |
|
521 FILE *f; |
|
522 int c; |
|
523 OBJ pos; |
|
524 extern int _immediateInterrupt; |
|
525 |
|
526 if (_INST(filePointer) != nil) { |
|
527 if (_INST(mode) != _writeonly) { |
|
528 f = MKFD(_INST(filePointer)); |
|
529 _immediateInterrupt = 1; |
|
530 if (_INST(unBuffered) == true) { |
|
531 if (read(fileno(f), &c, 1) != 1) |
|
532 c = EOF; |
|
533 } else { |
|
534 if (_INST(mode) == _readwrite) |
|
535 fseek(f, 0L, 1); /* needed in stdio */ |
|
536 c = getc(f); |
|
537 } |
|
538 _immediateInterrupt = 0; |
|
539 if (c != EOF) { |
|
540 pos = _INST(position); |
|
541 if (pos != nil) { |
|
542 _INST(position) = _MKSMALLINT(_intVal(pos) + 1); |
|
543 } |
|
544 if (_INST(binary) == true) { |
|
545 RETURN ( _MKSMALLINT(c & 0xFF) ); |
|
546 } |
|
547 RETURN ( _MKCHARACTER(c & 0xFF) ); |
|
548 } |
|
549 RETURN ( nil ); |
|
550 } |
|
551 } |
|
552 %} |
|
553 . |
|
554 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
555 self errorWriteOnly |
|
556 ! |
|
557 |
|
558 nextPut:aCharacter |
|
559 "write the argument, aCharacter - return nil if failed, self if ok" |
|
560 |
|
561 %{ /* NOCONTEXT */ |
|
562 |
|
563 FILE *f; |
|
564 char c; |
|
565 extern OBJ ErrorNumber; |
|
566 extern errno; |
|
567 int cnt; |
|
568 OBJ pos; |
|
569 extern int _immediateInterrupt; |
|
570 |
|
571 if (_INST(filePointer) != nil) { |
|
572 if (_INST(mode) != _readonly) { |
|
573 if (_isCharacter(aCharacter)) { |
|
574 c = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue); |
|
575 doWrite: |
|
576 f = MKFD(_INST(filePointer)); |
|
577 |
|
578 if (_INST(unBuffered) == true) { |
|
579 cnt = write(fileno(f), &c, 1); |
|
580 } else { |
|
581 if (_INST(mode) == _readwrite) |
|
582 fseek(f, 0L, 1); /* needed in stdio */ |
|
583 cnt = fwrite(&c, 1, 1, f); |
|
584 } |
|
585 if (cnt == 1) { |
|
586 if (_INST(unBuffered) == true) { |
|
587 fflush(f); |
|
588 } |
|
589 pos = _INST(position); |
|
590 if (pos != nil) { |
|
591 _INST(position) = _MKSMALLINT(_intVal(pos) + 1); |
|
592 } |
|
593 RETURN ( self ); |
|
594 } |
|
595 ErrorNumber = _MKSMALLINT(errno); |
|
596 RETURN ( nil ); |
|
597 } else { |
|
598 if (_INST(binary) == true) { |
|
599 if (_isSmallInteger(aCharacter)) { |
|
600 c = _intVal(aCharacter); |
|
601 goto doWrite; |
|
602 } |
|
603 } |
|
604 } |
|
605 } |
|
606 } |
|
607 %} |
|
608 . |
|
609 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
610 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
611 self argumentMustBeCharacter |
|
612 ! |
|
613 |
|
614 nextPutAll:aCollection |
|
615 "write all elements of the argument, aCollection" |
|
616 |
|
617 %{ /* NOCONTEXT */ |
|
618 |
|
619 FILE *f; |
|
620 unsigned char *cp; |
|
621 int len, cnt; |
|
622 extern OBJ ErrorNumber; |
|
623 extern errno; |
|
624 OBJ pos; |
|
625 extern int _immediateInterrupt; |
|
626 |
|
627 if (_INST(filePointer) != nil) { |
|
628 if (_INST(mode) != _readonly) { |
|
629 if (_isString(aCollection) || _isSymbol(aCollection)) { |
|
630 cp = _stringVal(aCollection); |
|
631 len = _stringSize(aCollection); |
|
632 f = MKFD(_INST(filePointer)); |
|
633 |
|
634 if (_INST(unBuffered) == true) { |
|
635 cnt = write(fileno(f), cp, len); |
|
636 } else { |
|
637 if (_INST(mode) == _readwrite) |
|
638 fseek(f, 0L, 1); /* needed in stdio */ |
|
639 cnt = fwrite(cp, 1, len, f); |
|
640 } |
|
641 if (cnt == len) { |
|
642 if (_INST(unBuffered) == true) { |
|
643 fflush(f); |
|
644 } |
|
645 pos = _INST(position); |
|
646 if (pos != nil) { |
|
647 _INST(position) = _MKSMALLINT(_intVal(pos) + len); |
|
648 } |
|
649 RETURN ( self ); |
|
650 } |
|
651 ErrorNumber = _MKSMALLINT(errno); |
|
652 RETURN ( nil ); |
|
653 } |
|
654 } |
|
655 } |
|
656 %} |
|
657 . |
|
658 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
659 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
660 |
|
661 aCollection do:[:element | |
|
662 self nextPut:element |
|
663 ] |
|
664 ! |
|
665 |
|
666 nextPut:aCollection from:start to:stop |
|
667 "write a range of elements of the argument, aCollection" |
|
668 |
|
669 %{ /* NOCONTEXT */ |
|
670 |
|
671 FILE *f; |
|
672 unsigned char *cp; |
|
673 int len, cnt, index1, index2; |
|
674 extern OBJ ErrorNumber; |
|
675 extern errno; |
|
676 extern int _immediateInterrupt; |
|
677 |
|
678 if (_INST(filePointer) != nil) { |
|
679 if (_isString(aCollection) |
|
680 && _isSmallInteger(start) |
|
681 && _isSmallInteger(stop)) { |
|
682 f = MKFD(_INST(filePointer)); |
|
683 cp = _stringVal(aCollection); |
|
684 len = _stringSize(aCollection); |
|
685 index1 = _intVal(start); |
|
686 index2 = _intVal(stop); |
|
687 if ((index1 < 1) || (index2 > len) || (index2 < index1)) { |
|
688 RETURN ( self ); |
|
689 } |
|
690 if (index2 > len) |
|
691 index2 = len; |
|
692 |
|
693 len = index2 - index1 + 1; |
|
694 if (_INST(unBuffered) == true) { |
|
695 cnt = write(fileno(f), cp + index1 - 1, len); |
|
696 } else { |
|
697 if (_INST(mode) == _readwrite) |
|
698 fseek(f, 0L, 1); /* needed in stdio */ |
|
699 cnt = fwrite(cp + index1 - 1, 1, len, f); |
|
700 } |
|
701 if (cnt == len) { |
|
702 if (_INST(unBuffered) == true) { |
|
703 fflush(f); |
|
704 } |
|
705 if (_INST(position) != nil) { |
|
706 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len); |
|
707 } |
|
708 RETURN ( self ); |
|
709 } |
|
710 ErrorNumber = _MKSMALLINT(errno); |
|
711 RETURN ( nil ); |
|
712 } |
|
713 } |
|
714 %} |
|
715 . |
|
716 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
717 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
718 |
|
719 start to:stop do:[:index | |
|
720 self nextPut:(aCollection at:index) |
|
721 ] |
|
722 ! |
|
723 |
|
724 cr |
|
725 "reimplemented for speed" |
|
726 |
|
727 %{ /* NOCONTEXT */ |
|
728 |
|
729 FILE *f; |
|
730 extern OBJ ErrorNumber; |
|
731 extern errno; |
|
732 extern int _immediateInterrupt; |
|
733 int cnt; |
|
734 |
|
735 if (_INST(filePointer) != nil) { |
|
736 if (_INST(mode) != _readonly) { |
|
737 f = MKFD(_INST(filePointer)); |
|
738 |
|
739 if (_INST(unBuffered) == true) { |
|
740 cnt = write(fileno(f), "\n", 1); |
|
741 } else { |
|
742 if (_INST(mode) == _readwrite) |
|
743 fseek(f, 0L, 1); /* needed in stdio */ |
|
744 cnt = fwrite("\n", 1, 1, f); |
|
745 } |
|
746 if (cnt == 1) { |
|
747 if (_INST(unBuffered) == true) { |
|
748 fflush(f); |
|
749 } |
|
750 if (_INST(position) != nil) { |
|
751 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1); |
|
752 } |
|
753 RETURN ( self ); |
|
754 } |
|
755 ErrorNumber = _MKSMALLINT(errno); |
|
756 return ( nil ); |
|
757 } |
|
758 } |
|
759 %} |
|
760 . |
|
761 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
762 self errorReadOnly |
|
763 ! ! |
|
764 |
|
765 !ExternalStream methodsFor:'more character I/O'! |
|
766 |
|
767 nextWord |
|
768 "in text-mode: |
|
769 read the next word (i.e. up to non letter-or-digit). |
|
770 return a string containing those characters. |
|
771 in binary-mode: |
|
772 read two bytes (msb-first) and return the value as a 16-bit unsigned Integer |
|
773 (msb-first for compatibility with other smalltalks)" |
|
774 |
|
775 %{ /* NOCONTEXT */ |
|
776 extern int _immediateInterrupt; |
|
777 |
|
778 if (_INST(binary) == true) { |
|
779 if (_INST(filePointer) != nil) { |
|
780 if (_INST(mode) != _writeonly) { |
|
781 FILE *f; |
|
782 int hi, low; |
|
783 |
|
784 f = MKFD(_INST(filePointer)); |
|
785 hi = getc(f); |
|
786 if (hi == EOF) { |
|
787 RETURN ( nil ); |
|
788 } |
|
789 low = getc(f); |
|
790 if (low == EOF) { |
|
791 if (_INST(position) != nil) { |
|
792 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1); |
|
793 } |
|
794 RETURN ( _MKSMALLINT(hi & 0xFF) ); |
|
795 } |
|
796 if (_INST(position) != nil) { |
|
797 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2); |
|
798 } |
|
799 RETURN ( _MKSMALLINT(((hi & 0xFF)<<8) | (low & 0xFF)) ); |
|
800 } |
|
801 } |
|
802 } |
|
803 %} |
|
804 . |
|
805 %{ |
|
806 FILE *f; |
|
807 int len; |
|
808 char buffer[1024]; |
|
809 int ch; |
|
810 int cnt = 0; |
|
811 extern int _immediateInterrupt; |
|
812 |
|
813 if (_INST(filePointer) != nil) { |
|
814 if (_INST(mode) != _writeonly) { |
|
815 f = MKFD(_INST(filePointer)); |
|
816 /* text-mode */ |
|
817 for (;;) { |
|
818 ch = getc(f); |
|
819 cnt++; |
|
820 |
|
821 if (ch >= ' ') break; |
|
822 if ((ch != ' ') && (ch != '\t') && (ch != '\r') |
|
823 && (ch != '\n') && (ch != 0x0b)) break; |
|
824 } |
|
825 ungetc(ch, f); |
|
826 cnt--; |
|
827 |
|
828 len = 0; |
|
829 for (;;) { |
|
830 ch = getc(f); |
|
831 if (ch == EOF) |
|
832 break; |
|
833 ch &= 0xFF; |
|
834 if (! (((ch >= 'a') && (ch <= 'z')) || |
|
835 ((ch >= 'A') && (ch <= 'Z')) || |
|
836 ((ch >= '0') && (ch <= '9')))) { |
|
837 ungetc(ch, f); |
|
838 break; |
|
839 } |
|
840 cnt++; |
|
841 buffer[len++] = ch; |
|
842 if (len >= sizeof(buffer)-1) { |
|
843 /* emergency */ |
|
844 break; |
|
845 } |
|
846 } |
|
847 if (_INST(position) != nil) { |
|
848 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + cnt); |
|
849 } |
|
850 buffer[len] = '\0'; |
|
851 if (len != 0) { |
|
852 RETURN ( _MKSTRING(buffer COMMA_CON) ); |
|
853 } |
|
854 RETURN ( nil ); |
|
855 } |
|
856 } |
|
857 %} |
|
858 . |
|
859 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
860 self errorWriteOnly |
|
861 ! |
|
862 |
|
863 nextWordPut:aNumber |
|
864 "only in binary-mode: |
|
865 write the argument, aNumber as two bytes (msb-first) |
|
866 (msb-first for compatibility with other smalltalks)" |
|
867 |
|
868 %{ /* NOCONTEXT */ |
|
869 |
|
870 int num; |
|
871 char bytes[2]; |
|
872 FILE *f; |
|
873 extern OBJ ErrorNumber; |
|
874 extern errno; |
|
875 extern int _immediateInterrupt; |
|
876 |
|
877 if (_INST(binary) == true) { |
|
878 if (_INST(filePointer) != nil) { |
|
879 if (_INST(mode) != _readonly) { |
|
880 if (_isSmallInteger(aNumber)) { |
|
881 num = _intVal(aNumber); |
|
882 bytes[0] = (num >> 8) & 0xFF; |
|
883 bytes[1] = num & 0xFF; |
|
884 |
|
885 f = MKFD(_INST(filePointer)); |
|
886 if (fwrite(bytes, 1, 2, f) == 2) { |
|
887 if (_INST(unBuffered) == true) { |
|
888 fflush(f); |
|
889 } |
|
890 if (_INST(position) != nil) { |
|
891 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 2); |
|
892 } |
|
893 RETURN ( self ); |
|
894 } |
|
895 ErrorNumber = _MKSMALLINT(errno); |
|
896 return ( nil ); |
|
897 } |
|
898 } |
|
899 } |
|
900 } |
|
901 %} |
|
902 . |
|
903 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
904 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
905 binary ifFalse:[^ self errorNotBinary]. |
|
906 self argumentMustBeInteger |
|
907 ! |
|
908 |
|
909 nextLong |
|
910 "in binary-mode: |
|
911 read two bytes (msb-first) and return the value as a 16-bit unsigned Integer |
|
912 (msb-first for compatibility with other smalltalks)" |
|
913 |
|
914 |lo hi| |
|
915 |
|
916 binary ifFalse:[ |
|
917 ^ self error:'method only valid in binary mode' |
|
918 ]. |
|
919 hi := self nextWord. |
|
920 lo := self nextWord. |
|
921 ^ hi * 16r10000 + lo |
|
922 ! |
|
923 |
|
924 nextLine |
|
925 "read the next line (characters up to newline). |
|
926 Return a string containing those characters excluding the newline. |
|
927 If the previous-to-last character is a cr, this is also removed, |
|
928 so its possible to read alien (i.e. ms-dos) text as well." |
|
929 |
|
930 %{ /* NOCONTEXT */ |
|
931 |
|
932 FILE *f; |
|
933 int len; |
|
934 char buffer[1024*16]; |
|
935 extern int _immediateInterrupt; |
|
936 char *rslt; |
|
937 |
|
938 if (_INST(filePointer) != nil) { |
|
939 if (_INST(filePointer) != _writeonly) { |
|
940 f = MKFD(_INST(filePointer)); |
|
941 _immediateInterrupt = 1; |
|
942 buffer[0] = 0; |
|
943 rslt = fgets(buffer, sizeof(buffer), f); |
|
944 _immediateInterrupt = 0; |
|
945 if (rslt != NULL) { |
|
946 len = strlen(buffer); |
|
947 if (_INST(position) != nil) { |
|
948 _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len); |
|
949 } |
|
950 /* remove EOL character */ |
|
951 if ((len != 0) && (buffer[len-1] == '\n')) { |
|
952 buffer[--len] = '\0'; |
|
953 } |
|
954 if ((len != 0) && (buffer[len-1] == '\r')) { |
|
955 buffer[--len] = '\0'; |
|
956 } |
|
957 RETURN ( _MKSTRING(buffer COMMA_CON) ); |
|
958 } |
|
959 } |
|
960 } |
|
961 %} |
|
962 . |
|
963 (mode == #writeonly) ifTrue:[^ self errorWriteOnly]. |
|
964 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
965 ^ nil |
|
966 ! |
|
967 |
|
968 nextPutLine:aString |
|
969 "write the characters in aString and append a newline" |
|
970 |
|
971 %{ /* NOCONTEXT */ |
|
972 |
|
973 FILE *f; |
|
974 int len, cnt; |
|
975 OBJ pos; |
|
976 char *s; |
|
977 extern OBJ ErrorNumber; |
|
978 extern errno; |
|
979 extern int _immediateInterrupt; |
|
980 |
|
981 if (_INST(filePointer) != nil) { |
|
982 if (_INST(mode) != _readonly) { |
|
983 if (_isString(aString)) { |
|
984 f = MKFD(_INST(filePointer)); |
|
985 s = (char *) _stringVal(aString); |
|
986 len = _stringSize(aString); |
|
987 |
|
988 if (_INST(unBuffered) == true) { |
|
989 cnt = write(fileno(f), s, len); |
|
990 } else { |
|
991 if (_INST(mode) == _readwrite) |
|
992 fseek(f, 0L, 1); /* needed in stdio */ |
|
993 cnt = fwrite(s, 1, len, f); |
|
994 } |
|
995 if (cnt == len) { |
|
996 if (_INST(unBuffered) == true) { |
|
997 cnt = write(fileno(f), "\n", 1); |
|
998 } else { |
|
999 cnt = fwrite("\n", 1, 1, f); |
|
1000 } |
|
1001 if (cnt == 1) { |
|
1002 pos = _INST(position); |
|
1003 if (pos != nil) { |
|
1004 _INST(position) = _MKSMALLINT(_intVal(pos)+len+1); |
|
1005 } |
|
1006 RETURN ( self ); |
|
1007 } |
|
1008 } |
|
1009 ErrorNumber = _MKSMALLINT(errno); |
|
1010 RETURN ( nil ); |
|
1011 } |
|
1012 } |
|
1013 } |
|
1014 %} |
|
1015 . |
|
1016 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
1017 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
1018 self argumentMustBeString |
|
1019 ! |
|
1020 |
|
1021 nextPutLinesFrom:aStream upToLineStartingWith:aStringOrNil |
|
1022 "used to copy large files |
|
1023 - read from aStream up to and including a line starting with aStringOrNil |
|
1024 and append it to self. If aStringOrNil is nil or not matched, |
|
1025 copy preceeds to the end" |
|
1026 |
|
1027 |srcFilePointer| |
|
1028 |
|
1029 (mode == #readonly) ifTrue:[^ self errorReadOnly]. |
|
1030 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
1031 srcFilePointer := aStream filePointer. |
|
1032 srcFilePointer isNil ifTrue:[^ aStream errorNotOpen]. |
|
1033 %{ |
|
1034 FILE *dst, *src; |
|
1035 char *matchString; |
|
1036 int matchLen = 0; |
|
1037 char buffer[1024*16]; |
|
1038 extern int _immediateInterrupt; |
|
1039 |
|
1040 if (_isSmallInteger(srcFilePointer)) { |
|
1041 if ((aStringOrNil == nil) |
|
1042 || _isString(aStringOrNil)) { |
|
1043 if (aStringOrNil != nil) { |
|
1044 matchString = (char *) _stringVal(aStringOrNil); |
|
1045 matchLen = _stringSize(aStringOrNil); |
|
1046 } |
|
1047 dst = MKFD(_INST(filePointer)); |
|
1048 src = (FILE *)_intVal(srcFilePointer); |
|
1049 for (;;) { |
|
1050 if (fgets(buffer, sizeof(buffer), src) == NULL) break; |
|
1051 if (fputs(buffer, dst) == EOF) break; |
|
1052 if (matchLen) { |
|
1053 if (strncmp(matchString, buffer, matchLen) == 0) |
|
1054 break; |
|
1055 } |
|
1056 } |
|
1057 if (_INST(unBuffered) == true) { |
|
1058 fflush(dst); |
|
1059 } |
|
1060 _INST(position) = nil; |
|
1061 RETURN ( self ); |
|
1062 } |
|
1063 } |
|
1064 %} |
|
1065 . |
|
1066 ^ self primitiveFailed |
|
1067 ! |
|
1068 |
|
1069 peekForLineStartingWith:aString |
|
1070 "read ahead for next line starting with aString; |
|
1071 return the line-string if found, nil otherwise.. |
|
1072 do not advance position i.e. nextLine will reread this line" |
|
1073 |
|
1074 (mode == #writeonly) ifTrue:[^ self errorWriteOnly]. |
|
1075 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
1076 %{ |
|
1077 FILE *f; |
|
1078 int l; |
|
1079 char buffer[1024*10]; |
|
1080 char *cp; |
|
1081 char *matchString; |
|
1082 int firstpos, lastpos; |
|
1083 extern int _immediateInterrupt; |
|
1084 |
|
1085 if (_isString(aString)) { |
|
1086 matchString = (char *) _stringVal(aString); |
|
1087 l = _stringSize(aString); |
|
1088 |
|
1089 f = MKFD(_INST(filePointer)); |
|
1090 firstpos = ftell(f); |
|
1091 for (;;) { |
|
1092 lastpos = ftell(f); |
|
1093 _immediateInterrupt = 1; |
|
1094 cp = fgets(buffer, sizeof(buffer), f); |
|
1095 _immediateInterrupt = 0; |
|
1096 if (cp == NULL) { |
|
1097 fseek(f, firstpos, 0); |
|
1098 RETURN ( nil ); |
|
1099 } |
|
1100 if (strncmp(cp, matchString, l) == 0) { |
|
1101 fseek(f, lastpos, 0); |
|
1102 break; |
|
1103 } |
|
1104 } |
|
1105 /* remove EOL character */ |
|
1106 cp = buffer; |
|
1107 while (*cp && (*cp != '\n')) cp++; |
|
1108 *cp = '\0'; |
|
1109 RETURN ( _MKSTRING(buffer COMMA_CON) ); |
|
1110 } |
|
1111 %} |
|
1112 . |
|
1113 self argumentMustBeString |
|
1114 ! |
|
1115 |
|
1116 peekForLineStartingWithAny:aCollectionOfStrings |
|
1117 "read ahead for next line starting with any of aCollectionOfStrings; |
|
1118 return the index in aCollection if found, nil otherwise.. |
|
1119 If no match, do not change position; otherwise advance right before the |
|
1120 matched line so that nextLine will return this line." |
|
1121 |
|
1122 |line startPos linePos index| |
|
1123 |
|
1124 (mode == #writeonly) ifTrue:[^ self errorWriteOnly]. |
|
1125 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
1126 startPos := self position. |
|
1127 [self atEnd] whileFalse:[ |
|
1128 linePos := self position. |
|
1129 line := self nextLine. |
|
1130 index := 1. |
|
1131 aCollectionOfStrings do:[:prefix | |
|
1132 (line startsWith:prefix) ifTrue:[ |
|
1133 self position:linePos. |
|
1134 ^ index |
|
1135 ]. |
|
1136 index := index + 1 |
|
1137 ] |
|
1138 ]. |
|
1139 self position:startPos. |
|
1140 ^ nil |
|
1141 ! ! |
|
1142 |
|
1143 !ExternalStream methodsFor:'testing'! |
|
1144 |
|
1145 atEnd |
|
1146 "return true, if position is at end" |
|
1147 |
|
1148 %{ /* NOCONTEXT */ |
|
1149 |
|
1150 FILE *f; |
|
1151 |
|
1152 if (_INST(filePointer) != nil) { |
|
1153 f = MKFD(_INST(filePointer)); |
|
1154 RETURN ( feof(f) ? true : false ); |
|
1155 } |
|
1156 %} |
|
1157 . |
|
1158 self errorNotOpen |
|
1159 ! ! |
|
1160 |
|
1161 !ExternalStream methodsFor:'closing'! |
|
1162 |
|
1163 close |
|
1164 "close the stream - tell operating system" |
|
1165 |
|
1166 filePointer isNil ifTrue:[^ self]. |
|
1167 lobby unregister:self. |
|
1168 self closeFile. |
|
1169 filePointer := nil |
|
1170 ! ! |
|
1171 |
|
1172 !ExternalStream methodsFor:'reimplemented for speed'! |
|
1173 |
|
1174 peekFor:aCharacter |
|
1175 "return true and move past if next == something. |
|
1176 - reimplemented for speed" |
|
1177 |
|
1178 %{ /* NOCONTEXT */ |
|
1179 |
|
1180 FILE *f; |
|
1181 int c; |
|
1182 int peekvalue; |
|
1183 extern int _immediateInterrupt; |
|
1184 |
|
1185 if (_isCharacter(aCharacter)) { |
|
1186 if (_INST(filePointer) != nil) { |
|
1187 peekvalue = _intVal(_CharacterInstPtr(aCharacter)->c_asciivalue); |
|
1188 f = MKFD(_INST(filePointer)); |
|
1189 c = getc(f); |
|
1190 if (c == peekvalue) { |
|
1191 RETURN ( true ); |
|
1192 } |
|
1193 ungetc(c, f); |
|
1194 RETURN ( false ); |
|
1195 } |
|
1196 } |
|
1197 %} |
|
1198 . |
|
1199 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
1200 ^ super peekFor:aCharacter |
|
1201 ! |
|
1202 |
|
1203 skipLine |
|
1204 "read the next line (characters up to newline) skip only; |
|
1205 return nil if EOF reached" |
|
1206 |
|
1207 %{ /* NOCONTEXT */ |
|
1208 |
|
1209 FILE *f; |
|
1210 char buffer[1024*10]; |
|
1211 extern int _immediateInterrupt; |
|
1212 |
|
1213 if (_INST(filePointer) != nil) { |
|
1214 if (_INST(mode) != _writeonly) { |
|
1215 f = MKFD(_INST(filePointer)); |
|
1216 if (fgets(buffer, sizeof(buffer), f) != NULL) { |
|
1217 RETURN ( self ); |
|
1218 } |
|
1219 RETURN ( nil ); |
|
1220 } |
|
1221 } |
|
1222 %} |
|
1223 . |
|
1224 filePointer isNil ifTrue:[^ self errorNotOpen]. |
|
1225 self errorWriteOnly |
|
1226 ! |
|
1227 |
|
1228 skipToAll:aString |
|
1229 "skip for the sequence given by the argument, aCollection; |
|
1230 return nil if not found, self otherwise. On a successful match, next read |
|
1231 will return characters of aString." |
|
1232 |
|
1233 |oldPos buffer l first idx| |
|
1234 |
|
1235 (aString isKindOf:String) ifTrue:[ |
|
1236 oldPos := self position. |
|
1237 l := aString size. |
|
1238 first := aString at:1. |
|
1239 buffer := String new:l. |
|
1240 [true] whileTrue:[ |
|
1241 (self nextBytes:l into:buffer) == l ifFalse:[ |
|
1242 self position:oldPos. |
|
1243 ^ nil |
|
1244 ]. |
|
1245 buffer = aString ifTrue:[ |
|
1246 self position:(self position - l). |
|
1247 ^ self |
|
1248 ]. |
|
1249 idx := buffer indexOf:first startingAt:2. |
|
1250 idx == 0 ifFalse:[ |
|
1251 self position:(self position - l + idx - 1) |
|
1252 ] |
|
1253 ] |
|
1254 ]. |
|
1255 ^ super skipFor:aString |
|
1256 ! |
|
1257 |
|
1258 skipSeparators |
|
1259 "skip all whitespace; next will return next non-white-space character |
|
1260 or nil if endOfFile reached. |
|
1261 - reimplemented for speed" |
|
1262 |
|
1263 %{ /* NOCONTEXT */ |
|
1264 |
|
1265 FILE *f; |
|
1266 REGISTER int c; |
|
1267 extern int _immediateInterrupt; |
|
1268 |
|
1269 if (_INST(filePointer) != nil) { |
|
1270 if (_INST(mode) != _writeonly) { |
|
1271 f = MKFD(_INST(filePointer)); |
|
1272 while (1) { |
|
1273 if (feof(f)) { |
|
1274 RETURN ( nil ); |
|
1275 } |
|
1276 c = getc(f); |
|
1277 if (c < 0) { |
|
1278 RETURN ( nil ); |
|
1279 } |
|
1280 switch (c) { |
|
1281 case ' ': |
|
1282 case '\t': |
|
1283 case '\n': |
|
1284 case '\r': |
|
1285 case '\b': |
|
1286 case '\014': |
|
1287 break; |
|
1288 default: |
|
1289 ungetc(c, f); |
|
1290 RETURN ( _MKCHARACTER(c & 0xFF) ); |
|
1291 } |
|
1292 } |
|
1293 } |
|
1294 } |
|
1295 %} |
|
1296 . |
|
1297 (mode == #writeonly) ifTrue:[^ self errorWriteOnly]. |
|
1298 self errorNotOpen |
|
1299 ! |
|
1300 |
|
1301 skipSeparatorsExceptCR |
|
1302 "skip all whitespace but no newlines; |
|
1303 next will return next non-white-space character |
|
1304 or nil if endOfFile reached. |
|
1305 - reimplemented for speed" |
|
1306 |
|
1307 %{ /* NOCONTEXT */ |
|
1308 |
|
1309 FILE *f; |
|
1310 int c; |
|
1311 extern int _immediateInterrupt; |
|
1312 |
|
1313 if (_INST(filePointer) != nil) { |
|
1314 if (_INST(mode) != _writeonly) { |
|
1315 f = MKFD(_INST(filePointer)); |
|
1316 while (1) { |
|
1317 if (feof(f)) { |
|
1318 RETURN ( nil ); |
|
1319 } |
|
1320 c = getc(f); |
|
1321 if (c < 0) { |
|
1322 RETURN ( nil ); |
|
1323 } |
|
1324 switch (c) { |
|
1325 case ' ': |
|
1326 case '\t': |
|
1327 case '\b': |
|
1328 break; |
|
1329 default: |
|
1330 ungetc(c, f); |
|
1331 RETURN ( _MKCHARACTER(c & 0xFF) ); |
|
1332 } |
|
1333 } |
|
1334 } |
|
1335 } |
|
1336 %} |
|
1337 . |
|
1338 (mode == #writeonly) ifTrue:[^ self errorWriteOnly]. |
|
1339 self errorNotOpen |
|
1340 ! |
|
1341 |
|
1342 nextChunk |
|
1343 "return the next chunk, i.e. all characters up to the next |
|
1344 non-doubled exclamation mark; undouble doubled exclamation marks. |
|
1345 - reimplemented for speed" |
|
1346 |retVal| |
|
1347 |
|
1348 filePointer isNil ifTrue:[ |
|
1349 ^ self errorNotOpen |
|
1350 ]. |
|
1351 %{ |
|
1352 FILE *f; |
|
1353 int done = 0; |
|
1354 REGISTER int c; |
|
1355 unsigned char peekC; |
|
1356 char *buffer, *newBuffer; |
|
1357 REGISTER int index; |
|
1358 int currSize; |
|
1359 int inComment, inString, inPrimitive = 0; |
|
1360 extern int _immediateInterrupt; |
|
1361 |
|
1362 f = MKFD(_INST(filePointer)); |
|
1363 /* |
|
1364 * skip spaces |
|
1365 */ |
|
1366 while (! done) { |
|
1367 if (feof(f)) { |
|
1368 RETURN ( nil ); |
|
1369 } |
|
1370 c = getc(f); |
|
1371 switch (c) { |
|
1372 case ' ': |
|
1373 case '\t': |
|
1374 case '\n': |
|
1375 case '\r': |
|
1376 case '\b': |
|
1377 case '\014': |
|
1378 break; |
|
1379 |
|
1380 case EOF: |
|
1381 RETURN ( nil ); |
|
1382 |
|
1383 default: |
|
1384 ungetc(c, f); |
|
1385 done = 1; |
|
1386 break; |
|
1387 } |
|
1388 } |
|
1389 |
|
1390 /* |
|
1391 * read chunk into a buffer |
|
1392 */ |
|
1393 buffer = (char *)malloc(3000); |
|
1394 currSize = 3000; |
|
1395 index = 0; |
|
1396 while (! feof(f)) { |
|
1397 /* do we have to resize the buffer ? */ |
|
1398 if ((index+2) >= currSize) { |
|
1399 newBuffer = (char *)malloc(currSize * 2); |
|
1400 bcopy(buffer, newBuffer, index); |
|
1401 free(buffer); |
|
1402 buffer = newBuffer; |
|
1403 currSize = currSize * 2; |
|
1404 } |
|
1405 c = getc(f); |
|
1406 if (c == '%') { |
|
1407 peekC = getc(f); |
|
1408 ungetc(peekC, f); |
|
1409 if (peekC == '{') { |
|
1410 inPrimitive++; |
|
1411 } else if (peekC == '}') { |
|
1412 inPrimitive--; |
|
1413 } |
|
1414 } else { |
|
1415 if (! inPrimitive) { |
|
1416 if (c == '!') { |
|
1417 c = getc(f); |
|
1418 if (c != '!') { |
|
1419 ungetc(c, f); |
|
1420 break; |
|
1421 } |
|
1422 } |
|
1423 } |
|
1424 } |
|
1425 if (c == EOF) break; |
|
1426 buffer[index++] = c; |
|
1427 } |
|
1428 buffer[index] = '\0'; |
|
1429 /* |
|
1430 * make it a string |
|
1431 */ |
|
1432 retVal = _MKSTRING(buffer COMMA_CON); |
|
1433 free(buffer); |
|
1434 %} |
|
1435 . |
|
1436 ^ retVal |
|
1437 ! ! |