The output of the following C program differs from what is expected from
a UNIX system.
Here is the program:
/* BEGIN */
#include "apue.h"
#define BSZ 48
int
main(void)
{
FILE *fp;
char buf[BSZ];
memset(buf, 'a', BSZ-2);
buf[BSZ-2] = '\0';
buf[BSZ-1] = 'X';
if ((fp = fmemopen(buf, BSZ, "w+")) == NULL)
err_sys("fmemopen failed");
printf("initial buffer contents: %s\n", buf);
fprintf(fp, "hello, world");
printf("before flush: %s\n", buf);
fflush(fp);
printf("after fflush: %s\n", buf);
printf("len of string in buf = %ld\n", (long)strlen(buf));
memset(buf, 'b', BSZ-2);
buf[BSZ-2] = '\0';
buf[BSZ-1] = 'X';
fprintf(fp, "hello, world");
fseek(fp, 0, SEEK_SET);
printf("after fseek: %s\n", buf);
printf("len of string in buf = %ld\n", (long)strlen(buf));
memset(buf, 'c', BSZ-2);
buf[BSZ-2] = '\0';
buf[BSZ-1] = 'X';
fprintf(fp, "hello, world");
fclose(fp);
printf("after fclose: %s\n", buf);
printf("len of string in buf = %ld\n", (long)strlen(buf));
return(0);
}
/* END */
This is what it prints in OpenBSD:
$ ./a.out
initial buffer contents:
before flush:
after fflush: hello, worldaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
len of string in buf = 46
after fseek: bbbbbbbbbbbbhello, worldbbbbbbbbbbbbbbbbbbbbbb
len of string in buf = 46
after fclose: hello, worldcccccccccccccccccccccccccccccccccc
len of string in buf = 46
This is what it is expected to print:
$ ./a.out
initial buffer contents:
before flush:
after fflush: hello, world
len of string in buf = 12
after fseek: bbbbbbbbbbbbhello, world
len of string in buf = 24
after fclose: hello, worldcccccccccccccccccccccccccccccccccc
len of string in buf = 46
It was supposed to write a '\0' (nul character) on the two first
writings after "hello, world", but this nul character is not written.
And only the nul character I explicitly wrote with the statement
buf[BSZ-2] = '\0';
is considered.
Changing "w+" to "w" in the call to fmemopen(3) produces the correct
behaviour.
However, the nul character that should be written at opening the memory
stream with fmemopen(3) is correctly written at the beginning of the
buffer, as you can see in the line
before flush:
on the output.
As informed by Kernigh[0], this is a bug in OpenBSD's fmemopen.c
at the following line:
st->len = (oflags & O_WRONLY) ? 0 : size;
We want st->len = 0, but it sets st->len = size.
The bug is how oflags & O_WRONLY is true for "w" and false for "w+";
it needs to be true for both "w" and "w+".
-- Kernigh
I cannot confirm if Kernigh's affirmation is correct.
[0]https://old.reddit.com/r/openbsd/comments/i8ml2q