ExtStream.st
changeset 1 a27a279701f8
child 2 6526dde5f3ac
equal deleted inserted replaced
0:aa2498ef6470 1:a27a279701f8
       
     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 ! !