将printf重定向到fopencookie

将printf重定向到fopencookie,c,printf,raspbian,glibc,stdio,C,Printf,Raspbian,Glibc,Stdio,在Linux上(Raspbian在Raspberry Pi上),我希望这样,我的C应用程序使用printf打印的任何内容都会在回调中发送回我 (不,我不是说用>一些_file.txt进行shell重定向。我是说一个C程序自己决定将标准输出发送到同一程序中的回调中(因此,printf) (是的,我真的很想这样做。我正在使用OpenGL制作一个全屏程序,并希望使用我自己的渲染代码向该程序中的用户呈现任何printf'd文本。用其他东西替换所有printf调用是不可行的。) 我觉得这应该很容易。这个问

在Linux上(Raspbian在Raspberry Pi上),我希望这样,我的C应用程序使用
printf
打印的任何内容都会在回调中发送回我

(不,我不是说用
>一些_file.txt
进行shell重定向。我是说一个C程序自己决定将
标准输出发送到同一程序中的回调中(因此,
printf

(是的,我真的很想这样做。我正在使用OpenGL制作一个全屏程序,并希望使用我自己的渲染代码向该程序中的用户呈现任何
printf
'd文本。用其他东西替换所有
printf
调用是不可行的。)

我觉得这应该很容易。这个问题在StackOverflow上已经有了变化,但我发现没有一个是完全相同的

我可以使用
fopencookie
获取一个
文件*
,该文件最终调用我的回调函数。到目前为止,一切顺利。挑战在于让
stdout
printf
去那里

我不能使用
freopen
,因为它需要字符串路径。我想重定向到的
文件*
不是文件系统上的文件,而是在运行时存在的

我无法使用
dup2
,因为
fopencookie
中的
文件*
没有文件描述符(
fileno
返回-1)

建议我可以简单地将
stdout
重新分配到我的新
文件*
:“stdin、stdout和stderr是普通变量,您可以像其他任何变量一样进行设置。”。这个几乎可以工作。使用
fprintf(stdout,“whatever”)
打印的任何内容都会转到我的回调,具有任何格式说明符的
printf
也会转到我的回调。但是,任何对
printf
的调用,只要字符串中没有格式说明符,都会转到“原始”标准输出

我怎样才能实现我想做的事

附言:我不在乎便携性。这将仅在我当前的环境中运行

#define _GNU_SOURCE                                                         
#include <stdio.h>                                                          
#include <unistd.h>                                                         
#include <assert.h>                                                         
#include <stdarg.h>                                                         
#include <alloca.h>                                                         
#include <string.h>                                                         


static ssize_t my_write_func (void * cookie, const char * buf, size_t size) 
{                                                                           
    fprintf (stderr, "my_write_func received %d bytes\n", size);            
    char * copy = (char*) alloca (size + 1);                                
    assert (copy);                                                          
    copy[size] = 0;                                                         
    strncpy (copy, buf, size);                                              
    fprintf (stderr, "Text is: \"%s\"\n", copy);                            
    fflush (stderr);                                                        
    return size;                                                            
}                                                                           


static FILE * create_opencookie ()                                          
{                                                                           
    cookie_io_functions_t funcs;                                            
    memset (&funcs, 0, sizeof (funcs));                                     
    funcs.write = my_write_func;                                            
    FILE * f = fopencookie (NULL, "w", funcs);                              
    assert (f);                                                             
    return f;                                                               
}                                                                           


int main (int argc, char ** argv)                                           
{                                                                           
    FILE * f = create_opencookie ();                                        
    fclose (stdout);                                                        
    stdout = f;                                                             

    // These two DO go to my callback:                                                                        
    fprintf (stdout, "This is a long string, fprintf'd to stdout\n");                           
    printf ("Hello world, this is a printf with a digit: %d\n", 123);

    // This does not go to my callback.
    // If I omit the fclose above then it gets printed to the console.
    printf ("Hello world, this is plain printf.\n");           
    fflush (NULL);                                                          
    return 0;                                                               
}               
定义GNU源
#包括
#包括
#包括
#包括
#包括
#包括
静态ssize_t my_write_func(void*cookie,const char*buf,size_t size)
{                                                                           
fprintf(stderr,“my_write_func接收到%d字节\n”,大小);
char*copy=(char*)alloca(大小+1);
断言(副本);
复制[大小]=0;
strncpy(副本、基本单位、大小);
fprintf(标准格式,“文本为:\%s\”\n”,副本);
fflush(stderr);
返回大小;
}                                                                           
静态文件*创建\u opencookie()
{                                                                           
cookie\u io\u函数\u t函数;
memset(&funcs,0,sizeof(funcs));
funcs.write=my_write_func;
文件*f=fopencookie(空,“w”,funcs);
断言(f);
返回f;
}                                                                           
int main(int argc,字符**argv)
{                                                                           
FILE*f=create_opencookie();
fclose(stdout);
stdout=f;
//这两个确实会转到我的回调:
fprintf(stdout,“这是一个长字符串,fprintf'd to stdout\n”);
printf(“你好,世界,这是一个数字为:%d\n”的printf,123);
//这不属于我的回拨。
//如果我省略上面的fclose,那么它将被打印到控制台。
printf(“你好,世界,这是纯printf。\n”);
fflush(空);
返回0;
}               

这似乎是GLIBC中的一个bug

printf(“简单字符串”)
printf(“foo%d”,123)
工作方式不同的原因是,GCC将前者转换为
put
,并认为它们是等价的

据我所知,它们应该是等价的。声明
输出放入
标准输出
,就像
printf
一样

然而,在GLIBC
printf
中,输出到
stdout
,但是
将输出放到
\u IO\u stdout
,这些是不等价的

要解决此错误,可以使用
-fno builtin printf
标志进行构建。这防止GCC将
printf
转换为
put
,并在我的系统上生成:

$ ./a.out
my_write_func received 126 bytes
Text is: "This is a long string, fprintf'd to stdout
Hello world, this is a printf with a digit: 123
Hello world, this is plain printf.
"
这个解决方法当然是不完整的:如果调用
put
setvbuf (f, NULL, _IOLBF, 0);  // line buffered
#include <pthread.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>

// this is the original program which you can't change
void print(void) {
    printf("Hello, %d\n", 123);
    puts("world");
    printf("xyz");
}

int p[2];

void *render(void *arg) {
    int nread;
    char buf[1];
    while((nread = read(p[0], buf, sizeof buf)) > 0) {
        // process the written data, in this case - make it uppercase and write to stderr
        for(int i = 0; i < nread; i++)
            buf[i] = toupper(buf[i]);
        write(2, buf, nread);
    }
    return NULL;
}

int main() {
    setvbuf(stdout, NULL, _IONBF, 0);
    pipe(p);
    dup2(p[1], 1);
    close(p[1]);
    pthread_t t;
    pthread_create(&t, NULL, render, NULL);
    print();
    close(1);
    pthread_join(t, NULL);
}