[comp.lang.c] bug in fopen/getc in Megamax C

DERMOTT@DREA-XX.arpa (Dave Dermott) (04/29/87)

   Bug in fopen/getc
System ATARI 1040 ST
Compiler Megamax v1.1
    Since this is a problem with a specific compiler/system
I don't know if this is the right place for this question but maybe it 
happens on other systems. 

     I open a file with fopen and read it with getc until it returns
EOF . Then I try to read another byte. After I close the file the size 
has increased by one byte even though it was opened for read-only !

 A real case happens in ST GEM KERMIT in function bufill() which is
based on an old C-KERMIT.  After sending a source file a garbage byte 
appeared at the end and caused an error the next time I compiled it.
I moved a feof() to before the getc() to fix it.
example:
----------------------------------
#include <stdio.h>
char *filnam;
FILE *fd;
int ic;
main(argc,argv)
int argc;
char *argv[];
{
if(argc < 2){
            puts("no filename");
            exit(1);}
filnam=argv[1];
puts(filnam);
fd=fopen(filnam,"r");  /*  strips \r */
/* fd=fopen(filnam,"br"); */      /*  binary read */
while(getc(fd) != EOF);     /* read to EOF */
ic=getc(fd);                /* get next byte */
printf("ic= %d\n",ic);  /* ic should be -1 */
fclose(fd);
}
-----------------------------------
     Create a small file and find its length. Run this program to
read the file then check the length again. It is one byte more.
    Note: On the Megamax fopen() the "br" option is a 'binary' read
which reads everything from the file . The 'normal' read "r" causes
Carriage-returns (\r) to be ignored when reading. Either way it still
increases the length.

    When I tried reading beyond the end-of-file with low level IO
( open() and read() ) the file length is not increased so it appears
the error is in fopen/getc/fclose.

    I don't have the sources of the IO library but they are written
in C and appear to be based on standard UNIX functions - getc() is
a macro :
#define getc(p) (--(p)->_cnt >= 0 ? *(p)->_ptr++ & 0377 : _fillbuf(p))
    Does this happen on any other system ? I only have access to
VAX-VMS C which has a quite different IO system .

David Dermott (DERMOTT@DREA-XX.ARPA)
DREA
Dartmouth Nova Scotia
-------

DERMOTT@DREA-XX.arpa (Dave Dermott) (05/03/87)

Re: bug in fopen/getc

System: ATARI 1040 ST
Compiler: Megamax C V1.1

   Earlier I noted the problem of attempting to read
beyond the end of file with getc() - it extended the length of the 
file. I tracked the problem down to a call to lseek() in fclose()
and fflush(). If one tries to position the file beyond the end of
file with lseek() the file is extended. 
Example:

#include <stdio.h>
#include <osbind.h>
#define O_RDONLY 0
extern long  lseek();
char *filnam;
int fd;
long ll;
main(argc,argv)
int argc;
char *argv[];
{
if(argc < 2){
            puts("no filename");
            exit(1);}
filnam=argv[1];
puts(filnam);
 fd=open(filnam,O_RDONLY);  /* open read only !! */
ll=lseek(fd,0L,2); /* go to end of file */
printf("eof  at %ld\n",ll); /*show file position */
ll=lseek(fd,20L,1); /* move forward 20 bytes */
 /* ll= Fseek(20L,fd,1); */  /*GEMDOS call $42 works ,returns error code */
printf("after seek %ld\n",ll); /* show new position */

close(fd);
}


 The file gets extended by 20 bytes!
 The GEMDOS call $42 - ( Fseek() not to be confused with fseek())
does the right thing. It returns a negative error code and doesn't
extend the file. Note the argument sequence is different.
  By disassembling  ( I don't have the source of lseek() )
I found   lseek() calls Fseek() and then does several other things,
including call  write() !! The read-only flag seems to be ignored .
  Seek and Ye shall find!
David Dermott

P.S.

Here is the reconstructed source of fclose/fflush , from decompiling
the disassembled object ( I haven't figured out lseek yet)
#include <stdio.h>
extern long lseek();

fclose(fp)
register FILE *fp;
{
if(fflush(fp))return (-1); 
if(fp->_flag & _BIGBUF)_disposptr(fp->_base); /* ie mfree() */
fp->_flag = 0;
if(close(fp->_fd))return (-1);
return 0;
}

fflush(fp)
register FILE *fp;
{
register len;
if(!((fp->_flag) & (_READ|_WRITE) ))return (-1);
len=fp->_ptr - fp->_base;
if((fp->_flag)&_DIRTY){
              if(!((fp->_flag) & _WRITE ))return (-1);
              if((fp->_flag) & _APPEND )lseek(fp->_fd,0L,2);
              if(write(fp->_fd, fp->_base, len) == -1)return(-1);
              fp->_flag &= ~ _DIRTY;
              fp->_mark += len;
              }
       else {
             if((fp->_fd) >0) fp->_mark =lseek(fp->_fd,(long)(-(fp->_cnt)),1);
/* if attempt has been made to read past EOF, fp->_cnt <0  so lseek
will position beyond EOF */
/* fix by putting in check for fp->cnt < 0 */
             }

       fp->_ptr = fp->_base;
       fp->_cnt = 0;

return (0);
}

-------