3

Can anyone explain the 'correct' semantics for ftell() when used on a memory stream.

Given the following program:

#include <stdio.h>
#include <stdlib.h>
#include <gnu/libc-version.h>

int main(void)
{
   puts (gnu_get_libc_version ());

   size_t n_buffer = 1024;
   char *buffer = calloc(n_buffer, sizeof(char));
   FILE *file = fmemopen(buffer, n_buffer, "w");

   /* "ABCD" */
   static const char magic_number[] = 
   {
     0x41, 0x42, 0x43, 0x44 
   };

   const size_t written = fwrite(magic_number, 1, 4, file);
   fprintf(stderr,"written=%d\n",written);

   int fstatus = fflush(file);
   fprintf(stderr,"fstatus=%d\n",fstatus);

   long ftellpos = ftell(file);
   fprintf(stderr,"ftellpos=%ld\n",ftellpos);

   fstatus = fseek(file, 0, SEEK_END);
   fprintf(stderr,"fstatus=%d\n",fstatus);

   ftellpos = ftell(file);
   fprintf(stderr,"ftellpos2=%ld\n",ftellpos);

   return 0;
}

The output on RHEL7 is:

2.17
written=4
fstatus=0
ftellpos=4
fstatus=0
ftellpos2=4

Whereas the output on OpenSUSE Leap 42 is:

2.22
written=4
fstatus=0
ftellpos=0
fstatus=0
ftellpos2=4

(This led to a unit test failure in code I was looking at)

My questions are:

  • Is the fseek() required (by a standard) to make the result of ftell() valid?
  • Is this a bug or change in behaviour of glibc?
  • Why doesn't it work on OpenSUSE?

The most obvious implementation is for the file position indicator to be an index in the memory buffer given to fmemopen. Its hard to see how that could go wrong.

Indeed the implementation:

https://github.com/bminor/glibc/blob/73dfd088936b9237599e4ab737c7ae2ea7d710e1/libio/fmemopen.c

Has c->pos = pos + s; at line 85.

And presumably ftell() just returns c->pos (in a roundabout way)

There has been some re-organisation of the glibc source source between 2.17 and 2.22 that would probably explain this if I could unravel it. But is it a bug or feature?

I'm not sure if the Posix and C standards fully specify whether ftell should work correctly for a memory stream. Intuitively its hard to see why it shouldn't be mandated as it ought to just work.

http://man7.org/linux/man-pages/man3/fmemopen.3.html

Says:

"The current position is implicitly updated by I/O operations. It can be explicitly updated using fseek(3), and determined using ftell(3)."

Other man pages mention that ftell might not have to work for things that aren't really files. However, I believe they really have devices in mind there.

Bruce Adams
  • 4,953
  • 4
  • 48
  • 111
  • So, what does the only authoritative resource about **standard** functions tell you? – too honest for this site Sep 15 '17 at 00:08
  • Sounds like a bug - [file one here](https://sourceware.org/bugzilla/enter_bug.cgi?product=glibc) – o11c Sep 15 '17 at 01:23
  • 1
    Although unlikely the issue, the casual integer type conversion done here add unnecessary questions to the post. Suggest to use the right type and print specifiers. `ftell()` returns `long` and `size_t` needs a `"zu"`. – chux - Reinstate Monica Sep 15 '17 at 05:16
  • @Olaf there are two authorative sources C and Posix. I believe both leave it undefined but I am not a language lawyer. Hence my question. – Bruce Adams Sep 15 '17 at 09:19
  • @o11c I believe you are correct. It is either a bug in the implementation (my belief) or a bug in the documentation or both. I've submitted a bug report here: https://sourceware.org/bugzilla/show_bug.cgi?id=22140 Lets see what the maintainers have to say. – Bruce Adams Sep 15 '17 at 10:35
  • There may also be a linked bug in the Posix specification itself http://austingroupbugs.net/view.php?id=1156 – Bruce Adams Sep 15 '17 at 12:09
  • "there are two authorative sources C and Posix" - No. POSIX refers to ISO9899 . Hence it inherits the C standard. And that one **is** clear (unless you forgot relevant info in the question). Read it, details are important. – too honest for this site Sep 15 '17 at 12:52
  • Memory streams are part of Posix not C. The C standard http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf does not say much about ftell() except to warn about the difference between binary and text files. If you think it is clear then please explain. Likewise http://pubs.opengroup.org/onlinepubs/9699919799/functions/fmemopen.html discusses seek behaviour but not ftell. – Bruce Adams Sep 19 '17 at 11:54

1 Answers1

0

Just found this quote on the net in a discussion:

The ftell() Open Group Base Specifications Issue 7 doc states ‘ftell() shall return the current value of the file-position indicator for the stream.’ The file position indicator is not updated without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), or until the buffer is full.

So, it looks like there is some buffer handling difference in rh and suse. You would need to flush the buffer in a way to read the correct position in the file.

Serge
  • 11,616
  • 3
  • 18
  • 28
  • I am well aware of that requirement. Note that there is a call to fflush after the write in my original example. – Bruce Adams Sep 15 '17 at 09:17
  • "I found this somewhere in the mud" is not really a good start for an answer. That does not answer if it is allowed to do or not (it is). – too honest for this site Sep 15 '17 at 12:53
  • @Olaf yes, i know, but it points to a doc and explains the behavior. – Serge Sep 15 '17 at 13:03
  • I don't see any "pointer" And it is wrong/missleading, too. Read the standard instead of obscure forums! – too honest for this site Sep 15 '17 at 13:07
  • @Olaf you are welcome to provide a quote from a standard. please to it. – Serge Sep 15 '17 at 13:33
  • How about you read 7.21.9.4 yourself? I'm not for spoon-feeding; own research will gain knowledge much more easily and anchor it better in the brain.. – too honest for this site Sep 15 '17 at 13:35
  • @Olaf for your information, there is absolutely nothing in the standard which would shed any light on the behavior of the ftell function as in the original question. – Serge Sep 15 '17 at 20:52
  • "the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read". And `ftell` returns a `long int`, not an `int`. Actually the compiler should emit a truncation warning, at least for 64 bit Linux. – too honest for this site Sep 15 '17 at 21:08