ISO/ IEC JTC1/SC22/WG14 N776

                 Document Number:  WG14 N776/J11 97-140

                               C9X Revision Proposal
                               =====================

       Title: fseek & ungetc

       Author: Fred J. Tydeman
       Author Affiliation: Tydeman Consulting
       Postal Address: 3711 Del Robles Dr., Austin, TX 78727-1814
       E-mail Address: [email protected]
       Telephone Number: +1 (512) 255-8696
       Fax Number: +1 (512) 255-8696

       Sponsor: NCITS/J11
       Date: 1997-09-25
       Document History: N/A.
       Proposal Category:
          __ Editorial change/non-normative contribution
          _Y Correction
          __ New feature
          __ Addition to obsolescent feature list
          __ Addition to Future Directions
          __ Other (please specify)  ______________________________
       Area of Standard Affected:
          __ Environment
          __ Language
          __ Preprocessor
          _Y Library
             __ Macro/typedef/tag name
             _Y Function
             __ Header
          __ Other (please specify)  ______________________________
       Prior Art: Many compilers already support one or the other
       interpretations.
       Target Audience: All users.
       Related Documents (if any):  SC22WG14.2595__________________
       Proposal Attached: _Y Yes __ No, but what's your interest?

       Abstract: Pick a defined behaviour for the interaction between
       fseek and ungetc for binary streams.

       Proposal:

       In 7.9.9.2 The fseek Function, change:

       "A successful call to the fseek function clears the
       end-of-file indicator for the stream and undoes any effects
       of the ungetc function on the same stream."

       to:

       Option A:

       "A successful call to the fseek function clears the
       end-of-file indicator for the stream, then undoes any
       effects of the ungetc function on the same stream, and then
       uses the current file position to set the new file position
       indicator."

       or to:

       Option B:

       "A successful call to the fseek function clears the
       end-of-file indicator for the stream, uses the current file
       position to determine the new file position indicator, and
       then undoes any effects of the ungetc function on the same
       stream."

       Rationale:

       The current wording in unclear as to two items:

       1) End-of-file is set, ungetc is done (which clears the
       end-of-file indicator), and then fseek of SEEK_CUR is done. 
       fseek has contradictory requirements: clear end-of-file
       indicator and undoes ANY effects of ungetc (which would set
       end-of-file indicator for this case). 

       2) The order is not specified for getting the current value
       of the file position indicator and undoing the effects of
       ungetc to determine the new file position indicator. 

       I am offering both choices on how to fix it as we could not
       reach informal consensus from the few who responded via
       email at around SC22WG14.2595 (June, 1996). 

       I believe that option A is the correct behaviour based upon
       what ungetc does for fseek.  fseek causes ungetc to discard
       any pushed back characters and ungetc sets the value of the
       file position indicator to be the same after discarding all
       pushed-back characters as it was before the characters were
       pushed back.

       Here are two programs to show the interactions:

/*
 * Question on meaning of fseek after ungetc with binary files.
 *
 * 7.9.9.2 The fseek Function says 
 *
 *   "A successful call to the fseek function clears the end-of-file
 *   indicator for the stream and undoes any effects of the ungetc 
 *   function on the same stream."
 *
 * 7.9.7.11 The ungetc Function says
 *
 *   "A successful intervening call (with the stream pointed to by stream)
 *   to a file positioning function (fseek, fsetpos, or rewind) discards
 *   any pushed-back characters for the stream."
 *
 *   "The value of the file position indicator for the stream after
 *   reading or discarding all pushed-back characters shall be the same
 *   as it was before the characters were pushed back."
 *
 *   "For a binary stream, its file position indicator is decremented
 *   by each successful call to the ungetc function; ..."
 *
 * The question is:  
 *
 * When one does a relative seek (SEEK_CUR) just after an ungetc, what is
 * the current value of the file position indicator?  Is it the value at
 * the time fseek is entered, or is it the value after the effects of
 * ungetc have been undone?  Or, something else?
 *
 * In terms of this program, is the final print a 7 or 8?
 *
 * The three ftell's print:
 *  8, 8, 8  Borland Turbo C 2.0, Symantec C/C++ 7
 *  8, 7, 8  Borland C/C++ 4.0
 *  8, -, -  emx 08.h (Cannot ungetc)
 *  8, 7, 7  emx 09.b, 09.c
 */

#include <stdio.h>	/* printf(), ftell(), fseek(), ungetc(), FILE */
#include <errno.h>	/* errno */

int main(void){
  long pos0 = 8L;
  long pos1;
  long pos2;
  long pos3;
  int rc;
  int c;
  FILE *f;

  f = tmpfile();		/* create a temp binary file */
	if( NULL == f ){ perror("cannot tmpfile"); goto death; }

  rc = fputs("0123456789", f);	/* write some chars to that file */
	if( EOF == rc ){ perror("cannot fputs"); goto death; }

  rc = fflush(f);		/* make sure it is written */
	if( EOF == rc ){ perror("cannot fflush"); goto death; }

	errno = 0;
  rewind(f);			/* set file position indicator */
	if( errno ){ perror("cannot rewind"); goto death; }

  rc = fseek(f,pos0,SEEK_SET);	/* set file position indicator */
	if( rc ){ perror("cannot fseek"); goto death; }

  pos1 = ftell(f);		/* should be 8 */
	if( -1L == pos1 ){ perror("cannot ftell"); goto death; }
  (void)printf("...After seek to %ld, ftell says %ld\n", pos0, pos1 );

  c = ungetc('x',f);		/* decrement file position indicator */
	if( EOF == c ){ perror("cannot ungetc"); goto death; }

  pos2 = ftell(f);		/* should be 7 */
	if( -1L == pos2 ){ perror("cannot ftell"); goto death; }
  (void)printf("...After ungetc, ftell says %ld; should be %ld\n",
		pos2, pos1-1L );

  rc = fseek(f,0L,SEEK_CUR);	/* seek relative to current file position */
	if( rc ){ perror("cannot fseek"); goto death; }

  pos3 = ftell(f);		/* what is it? 7 or 8 */
	if( -1L == pos3 ){ perror("cannot ftell"); goto death; }
  (void)printf("...After relative seek of 0, ftell says %ld\n", pos3 );

death:;
  return 0;
}



/*
 * Question on meaning of: At end of file, ungetc, then fseek current.
 *
 * 7.9.9.2 The fseek Function says 
 *
 *   "A successful call to the fseek function clears the end-of-file
 *   indicator for the stream and undoes any effects of the ungetc 
 *   function on the same stream."
 *
 * 7.9.7.11 The ungetc Function says
 *
 *   "A successful call to the ungetc functions clears the end-of-file
 *   indicator for the stream."
 *
 * The question is:  
 * 
 * If one is at the end-of-file (so the end-of-file indicator is set),
 * one does an ungetc (which clears the end-of-file indicator), then one
 * does a fseek to the current position, is the end-of-file indicator set?
 * (Assumes the file position indicator for the stream after discarding
 * all pushed-back characters is the same as it was before the characters
 * were pushed back is the effect of fseek after ungetc.  Assumes can ungetc
 * while end-of-file is true.)
 *   "undoes any effects of the ungetc function" implies set (since ungetc
 *            cleared end-of-file and that effect is now undone)
 *   "fseek function clears the end-of-file indicator" implies clear.
 * Seems like a contradiction.
 *
 * The three ftell's and EOF print:
 *  10, 10,  -,     - Borland Turbo C 2.0 (EOF still set after ungetc)
 *  10, 10,  -,     - Symantec C/C++ 7 (cannot ungetc at EOF)
 *  10, 10, 10, False Borland C/C++ 4.0
 *  10, 10,  9, False emx 08.h, 09.b, 09.c
 */
#include <stdio.h>	/* printf(), ftell(), fseek(), ungetc(), FILE */
#include <errno.h>	/* errno */

int main(void){
  long pos1;
  long pos2;
  long pos3;
  int eof1;
  int eof2;
  int eof3;
  int err1;
  int err2;
  int err3;
  int rc;
  int c;
  FILE *f;
  static const char b[2][6] = { {"False"}, {"True "} };

  f = tmpfile();		/* create a temp binary file */
	if( NULL == f ){ perror("cannot tmpfile"); goto death; }

  rc = fputs("0123456789", f);	/* write some chars to that file */
	if( EOF == rc ){ perror("cannot fputs"); goto death; }

  rc = fflush(f);		/* make sure it is written */
	if( EOF == rc ){ perror("cannot fflush"); goto death; }

	errno = 0;
  rewind(f);			/* go to start of file */
	if( errno ){ perror("cannot rewind"); goto death; }

  rc = fseek(f,9L,SEEK_SET);	/* go to just before end of file */
	if( rc ){ perror("cannot fseek to before end"); goto death; }
 
  c = fgetc(f);			/* get to end-of-file */
	if( '9' != c ){ perror("fgetc of '9' failed"); goto death; }

  pos1 = ftell(f);		/* should be 10? */
	if( -1L == pos1 ){ perror("cannot ftell"); goto death; }
  (void)printf("...After seek to end, ftell says %ld\n", pos1 );
 
  c = fgetc(f);			/* get at end-of-file */
	if( EOF != c ){ perror("fgetc at EOF not EOF"); goto death; }

  eof1 = feof(f);		/* make sure at EOF */
	if( 0 == eof1 ){ perror("EOF not set at EOF"); goto death; }

  err1 = ferror(f);		/* make sure not past EOF */
  	if( 0 != err1 ){ perror("went past EOF"); goto death; }

  pos2 = ftell(f);		/* should be 10 or 11? */
	if( -1L == pos2 ){ perror("cannot ftell"); goto death; }
  (void)printf("...After fgetc, ftell says %ld\n", pos2 );

  c = ungetc('x',f);		/* reset end-of-file indicator */
	if( EOF == c ){ perror("cannot ungetc at EOF"); goto death; }

  eof2 = feof(f);		/* make sure not at EOF */
	if( 0 != eof2 ){ perror("EOF still set after ungetc"); goto death; }

  err2 = ferror(f);		/* make sure not past EOF */
  	if( 0 != err2 ){ perror("error after ungetc"); goto death; }

  rc = fseek(f,0L,SEEK_CUR);	/* seek to where we are */
	if( rc ){ perror("cannot fseek"); goto death; }

  eof3 = feof(f);		/* What is EOF indicator? */
  (void)printf("...EOF after fseek after ungetc after EOF is: %s\n", 
		b[!!eof3] );

  err3 = ferror(f);		/* What is ERROR indicator? */
  (void)printf("...ERROR after fseek after ungetc after EOF is: %s\n", 
		b[!!err3] );

  pos3 = ftell(f);		/* What is position? */
  (void)printf("...Final ftell is %ld\n", pos3 );

death:;
  return 0;
}