我尝试使用FILE
界面创建类似文件的对象
fopencookie的。
我的目标是拥有固定大小的缓冲区,并始终拥有前缀" HDR"在 每条印刷线的开头。
例如,如果用户代码用于 p>
fprintf(fd, "hello there\n");
fprintf(fd, "how are you?\n");
fprintf(fd, "let's try again...\n");
我想打印
HDRhello there
HDRhow are you?
HDRlet's try again...
为了实现这一点,我做了一个小型原型,它使用了 fopencookie提供一个固定大小的缓冲区,移位尽可能多的字符 需要存储标题" HDR"。
struct blah
{
FILE* fd;
char* buffer;
};
void blah_init(struct blah* blah, size_t len)
{
blah->buffer = calloc(len + 1, sizeof(char));
blah->buffer[len] = 0;
int hdrlen = snprintf(blah->buffer, len + 1, "HDR");
cookie_io_functions_t cbacks = {
.write = mywrite,
.close = myclose,
};
blah->fd = fopencookie(blah, "w", cbacks);
setvbuf(blah->fd, blah->buffer + hdrlen, _IOFBF, len - hdrlen);
}
为了看看我打印时会发生什么,我添加了mywrite
的虚拟实现
和myclose
。
ssize_t mywrite(void* cookie, const char* buf, size_t len)
{
struct blah* blah = (struct blah*) cookie;
fprintf(stderr, "contains: \"%s\" (%p)\n", buf, buf);
fprintf(stderr, "actually: \"%s\" (%p)\n", blah->buffer, blah->buffer);
return len;
}
int myclose(void* cookie)
{
free(((struct blah*)cookie)->buffer);
return 0;
}
所以我希望contans:
后跟用户打印的字符串
代码,actually:
后跟相同的字符串,前缀为
报头中。
换句话说,鉴于此主要内容:
int main()
{
struct blah blah;
blah_init(&blah, 20);
fputs("goat chee", blah.fd);
fputs("se or ", blah.fd);
fflush(blah.fd);
fputs("goat soup", blah.fd);
fclose(blah.fd);
return 0;
}
我希望看到
contains: "goat chee" (x + 3)
actually: "HDRgoat chee" (x)
contains: "se or " (x + 3)
actually: "HDRse or " (x)
contains: "goat soup" (x + 3)
actually: "HDRgoat soup" (x)
虽然令人惊讶但我得到......
contains: "goat chee" (0x400a40)
actually: "HDR" (0x51ef040)
contains: "se or " (0x51ef043)
actually: "HDRse or " (0x51ef040)
contains: "goat soup" (0x51ef043)
actually: "HDRgoat soup" (0x51ef040)
所以,显然第一次调用我的写函数就是作弊和使用 一些内部缓冲区,而不是我提供的缓冲区。
这是正常行为吗?难道我做错了什么? glibc bug?
以下是完整的代码,如果有人想尝试的话。
我目前在Fedora 27下使用glibc 2.26运行它
(glibc-2.26-27.fc27.x86_64
)
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
struct blah
{
FILE* fd;
char* buffer;
};
ssize_t mywrite(void* cookie, const char* buf, size_t len)
{
struct blah* blah = (struct blah*) cookie;
fprintf(stderr, "contains: \"%s\" (%p)\n", buf, buf);
fprintf(stderr, "actually: \"%s\" (%p)\n", blah->buffer, blah->buffer);
return len;
}
int myclose(void* cookie)
{
free(((struct blah*)cookie)->buffer);
return 0;
}
void blah_init(struct blah* blah, size_t len)
{
blah->buffer = calloc(len + 1, sizeof(char));
blah->buffer[len] = 0;
int hdrlen = snprintf(blah->buffer, len + 1, "HDR");
cookie_io_functions_t cbacks = {
.write = mywrite,
.close = myclose,
};
blah->fd = fopencookie(blah, "w", cbacks);
setvbuf(blah->fd, blah->buffer + hdrlen, _IOFBF, len - hdrlen);
}
int main()
{
struct blah blah;
blah_init(&blah, 20);
fputs("goat chee", blah.fd);
fputs("se or ", blah.fd);
fflush(blah.fd);
fputs("goat soup", blah.fd);
fclose(blah.fd);
return 0;
}