www.delorie.com/djgpp/bugs/show.cgi   search  
Bug 000068

When Created: 03/15/1996 10:00:59
Against DJGPP version: 2.00
By whom: t.s.abbott@larc.nasa.gov
Abstract: fscanf != EOF (fscanf may not return an EOF)
Subject: fscanf not returning an EOF (22 Feb 96 library build)

I don't know if this is a bug or just an ill-defined ANSI C
problem, but fscanf may not return an EOF.  This was not
seen in the older build of the library (1 Jan 96), in versions
1.12 or 1.11, or in Borland C.

The effect of this problem is (obviously), the program crashing
and, I my case, crashing so badly that 'symify' would not
generate any usable output.  (Note that I later forced a similar
crashing in 1.12 by not checking for an EOF, and 'symify' gave
me valid output...  but that's another issue.)

The test case:
------------------------------------------------------------
#include <conio.h>
#include <dos.h>
#include <ctype.h>
#include <float.h>
#include <math.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(void)
{ FILE *outfile;
  FILE *infile;
  float f;
  outfile = fopen("test.dat","wt") ;
  if (outfile != NULL)
  { fprintf(outfile,"%f\n",21.1f) ;
    fclose(outfile);
  }
  infile = fopen("test.dat","r") ;
  if (infile != NULL) 
  {
    if (fscanf(infile,"%f",&f) == EOF) printf("eof at 1\n");
    else if (fscanf(infile,"%f",&f) == EOF) printf("eof at 2\n");
    else printf("never got an EOF\n");
    fclose(infile);
  }
} 
------------------------------------------------------------
This was compiled (in all instances) with no optimization.  In
all cases except the latest DJGPP 2.0 (22 Feb libraries), the
output was: 
"eof at 2"  
In the latest version, the output was:
"never got an EOF"

The workaround I've added was to test for both an EOF and
that fscanf's output > 0.

Workaround added: 03/15/1996 10:02:19
By whom: t.s.abbott@larc.nasa.gov
As described in the bug report, you may need to include a test for fscanf() > 0.

Note added: 03/25/1996 10:13:35
By whom: Broeker@axpmgr.rwth-aachen.de
When searching for a bug inflicting f2c, which occurs only for the
latest builds of DJGPP's libc, I also found this bug. The exact bug
is: fscanf (and scanf and sscanf, too, I think) do *not* return EOF
if the input is empty (i.e. the file is at EOF, or the string's end
has been reached). I wrote this short bug demonstration program:

-------- begin tfscanf.c ---------------
/*
 * HBB: Display misbehaviour of fscanf() at EOF
 * call this file tfscanf.c, so it can read its own source as
 * example input
 */
#include <stdio.h>
#include <assert.h>

int main()
{
    FILE *in=fopen("tfscanf.c", "rt");
    int c, d;

    while ((c=getc(in))!=EOF)
        ;
    assert(feof(in));
    /* Now fscanf's input is sure empty, so it should return EOF */
    if (EOF==(c=fscanf(in,"%d", &d))) {
        printf("OK, fscanf result is EOF\n");
        return 0;
    } else {
        printf("Error! fscanf results is %d ('%c') instead of EOF!\n", c, c);
        return 1;
    }
}
--------- end tfscanf.c --------------

Upon further examination, this is what I found: (including a fix, which I
will add as a "solution" seperately)

I do not have library sources of V2 earlier than the latest release
version (Feb, 22nd or so) at hand, but as there have been made only four
changes (counted in units of 'patch hunks') in the relevant source file,
doscan.c, since the 1.12maint3 version, which I do have here, the error
was easy to track down to these lines, which have been commented out
recently, but used to be active:

------- snippet from doscan.c dated Jan 24, 1996, line 106 ff ---------
/* breaks %n */
/*    if (fileended) {
      return(nmatch? nmatch: -1);
    } */
------- end snippet --------------------------------

If 'fileended' was set (by _innum or _instr), that indicates that doscan
has hit EOF when trying to read characters. In such a situation, all my
books say that fscanf *has* to end immediately, and return EOF (-1) if
this happened for the first 'conversion specifier' or the number of
previous successfully scanned input items otherwise.
Now, how and why could this 'break %n', as the comment in doscan.c says?
The relevant case is, if I got that right, the one when the just-scanned
format-specifier (say, a "%d"), has exactly used up all the rest of the
input file, and encountered EOF only because it had to 'snoop ahead' to
find out if there are still more digits to come. In this situation,
'fileended' will be set as well by the current code in _innum(). But
now, a subsequent "%n" specifier would indeed have to be handled. That
wouldn't have worked with the original, now commented-out code, which
would have terminated fscanf before that "%n" directive was ever seen.

But there's a difference: the return value of '_innum()' or '_instr()'
is zero if there were no characters in the input that matched the
format specifier. So the correct thing seems to be to re-activate
those lines of code quoted above, but only if the sub-functions of
doscan.c returned zero. That change indeed fixes the original bug
(but I haven't actually tested if it also keeps "%n" working, so
watch out).

Summing it up, I think this bug was introduced on Jan 24, 1996, in
trying to make "%n" work as the last format string component actually
used. 

Stay tuned for Solution Submittal.

Workaround added: 03/25/1996 10:28:42
By whom: Broeker@axpmgr.physik.rwth-aachen.de
To work around this bug, replace all expressions similar to

   (fscanf(infile,.....)==EOF)

by expressions like this:

   ((fscanf(infile, ....) < 1) && feof(infile))

But then, there's also the 'real solution', which I will
now submit

Solution added: 03/25/1996 10:36:22
By whom: Broeker@axpmgr.physik.rwth-aachen.de
OK, so here's my (attempt at) a patch to actually fix this bug:

----------- begin doscan.dif ---------------
*** doscan.c_o  Mon Mar 25 12:36:52 1996
--- doscan.c    Mon Mar 25 12:37:44 1996
***************
*** 104,112 ****
               &fileended) && ptr)
        nmatch++;
  /* breaks %n */
! /*    if (fileended) {
        return(nmatch? nmatch: -1);
!     } */
      break;    
    case ' ':
    case '
':
--- 104,112 ----
               &fileended) && ptr)
        nmatch++;
  /* breaks %n */
!     else if (fileended) { /* HBB: if conversion failed, stop action immediately*/
        return(nmatch? nmatch: -1);
!     }
      break;
    case ' ':
    case '
':
--------------- end doscan.dif -------------

That's it for now. Have fun with it, and tell me in case my analysis
was wrong, and maybe even why.

Hans-Bernhard Broeker (Aachen, Germany)
EMail: Broeker@axpmgr.physik.rwth-aachen.de

Solution added: 05/28/1996 14:14:13
By whom: Broeker@axpmgr.physik.rwth-aachen.de
My first solution was incorrect. After some discussion has taken
place, here's what we came up with (in my version, including comments
to document the changes). As with the previous version of the patch,
you'll have to know how to patch a library function to make use of
this patch. I could also provide a uuencoded binary doscan.o, if
anyone's actually interested in that.

--- dosc_v2.c   Wed Jan 24 04:31:46 1996
+++ dosc_hbb.c  Thu Apr 11 10:40:50 1996
@@ -101,13 +101,14 @@
     if (ch == '\0')
       return(-1);
     if (_innum(ptr, ch, len, size, iop, scan_getc, scan_ungetc,
-              &fileended) && ptr)
-      nmatch++;
-/* breaks %n */
-/*    if (fileended) {
-      return(nmatch? nmatch: -1);
-    } */
-    break;
+               &fileended)) {  /* HBB: returns true on successful match */
+      if (ptr)
+        nmatch++;        /* HBB: successful match, and no 'dummy' "%*" spec */
+      break;             /* HBB: success, so go on scanning */
+    } else if (fileended && !nmatch)
+      return(-1);        /* HBB: input failure before any successful match, requests EOF-return */
+    else
+      return(nmatch);    /* HBB: 'normal' failure (matching ~, or input ~ after some successful match(es) */
   case ' ':
   case '
':
   case '	': 
@@ -129,7 +130,7 @@
     if (ch1 != EOF) nchars++;
     if (ch1 != ch) {
       if (ch1==EOF)
-       return(-1);
+        return(nmatch?nmatch:-1); /* HBB: only return -1 if *no* success at all */
       scan_ungetc(ch1, iop);
       nchars--;
       return(nmatch);

Happy hacking

Solution added: 08/24/1996 20:09:16
By whom: rhawes@dma.org
The bug is in _doscan which is called by fscanf, scanf and sscanf.
Each return statement in _doscan should be:
return (nmatch ? nmatch : EOF)
This should correct the bug in all three functions.

Fixed in version on 04/18/1999 10:00:53
By whom: eliz@is.elta.co.il



  webmaster   donations   bookstore     delorie software   privacy  
  Copyright 2010   by DJ Delorie     Updated Jul 2010