Implement fmemopen

Eric Blake ebb9@byu.net
Thu Jul 26 02:49:00 GMT 2007


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to duane ellis on 7/20/2007 5:32 AM:
> Eric Blake wrote:
>> So my implementation needs to be made a bit smarter - when in
>> write-only mode, remember the character being overwritten by the
>> trailing NUL, then when seeking to a different location, restore that
>> byte (ie. the trailing NUL should always correspond to the current
>> position, rather than being a permanent artifact of writing).
> 
> Huh? That seems odd - what if I am writing bin bytes to the memory
> stream that purposely contain a NULL.
> 
> I do not have the POSIX docs - nor have I read them - but my gut tells
> me - I should be able to write bin data to this stream.

The draft POSIX wording requires that any time you fflush() or fclose() a
write-only memstream (whether by fmemopen with mode "w" or "a", or by
open_memstream), that the byte corresponding to the current position be
set to NUL (or in the case of fmemopen, if the current position is eof,
the last byte in the array; open_memstream has no effective eof), so that
you are guaranteed to have a NUL-terminated string.  POSIX does not
prohibit binary data, with embedded NULs, nor does my implementation.
Likewise, for read-write streams (such as fmemopen with mode "w+"), the
requirement is slightly different - a NUL is only written at the current
position (necessarily eof) when the write extends where eof is located.

One problem, then, comes in with enforcing this requirement when no writes
took place.  If you do fflush(fmemopen(array, 1, "w")), then according to
the POSIX rules, array[0] must be '\0' regardless of what it was before
the fmemopen, since the "w" stream was closed while the file position was
at 0.  But the way the newlib stdio routines are set up, calling fflush()
when there is no buffered write waiting to flush will not trigger any
write callbacks to the cookie owner.  So to meet the POSIX requirement, I
have to set offset 0 to '\0' during fmemopen if the stream mode starts
with 'w'.

Another problem is with seeks, since all memstreams are seekable.  If you do:
 char array[] = "foo";
 FILE *f=fmemopen(array, 4, "a"); // include space for trailing NUL
 fseek(f, 0, SEEK_SET);
 fflush(f);
then array[0] must be set to '\0' according to POSIX rules, since the
current position is 0.  Again, there was no buffered write before the
flush.  So the action of seeking should be setting the current byte to
NUL, if the seek destination lies before the end of the stream, under the
assumption that fflush() or fclose() will be called next.

On the other hand, if you do:
 char array[] = "12345";
 FILE *f=fmemopen(array, 5, "w");
 fwrite("boo", 3, 1, f);
 fflush(f);
 fseek(f, 0, SEEK_SET);
 fputc('f', f);
 fseek(f, 2, SEEK_CUR);
at this point, the newlib routines have called the write callback twice -
once for the fflush, and once for the second fseek, and the second fseek
calls both the write callback (to flush the 'f') and the seek callback (to
change the position to 3).  The write callback doesn't know which routine
calls it, so it must behave the same either way, and assume that it was
called by fflush, so in my implementation, the array contents are now 'f',
'\0', 'o', '\0', '5'.  But continuing the example,
 fflush(f);
since there was no fflush between the fputc and the second fseek, POSIX
does not permit the NUL in offset 1; rather, the only NUL must be at
offset 3 corresponding to the current position at the time of the fflush.
 Since there was no explicit write action, the fflush does not call the
write callback.  So the only solution is to make the write and seek
callbacks remember what they are overwriting when inserting a NUL prior to
eof, then make the seek callback check whether the current position before
the seek is prior to eof, in which case, it needs to restore whatever byte
had previously been temporarily set to NUL.

My current implementation (although I'm still testing it), with the
semantics I described above, is attached.  Changes from my original
submission are the addition of the saved character (as manipulated in the
write and seek callback), the reader now returns EOF if reading beyond
eof, and the writer ensures that any bytes skipped between eof and the
current position are properly set to NUL.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             ebb9@byu.net
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGoK3c84KuGfSFAYARApYmAKC1IDqm0TdqHFQXmZV5iPwLtRCbpgCg1/Vm
4eOhegiXo1Cq35y4FtTJnS4=
=lMHe
-----END PGP SIGNATURE-----
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: newlib.patch23
URL: <http://sourceware.org/pipermail/newlib/attachments/20070726/2bf1a769/attachment.ksh>


More information about the Newlib mailing list