On Linux (Raspbian on a Raspberry Pi) I would like to make it so that anything my C application prints using printf
is sent back to me in a callback.
(No, I'm not talking about shell redirection with > some_file.txt
. I'm talking about a C program making the decision by itself to send stdout
(and therefore printf
) to a callback within that same program.)
(Yes, I really do want to do this. I'm making a full-screen program using OpenGL and want to present any printf
'd text to the user within that program, using my own rendering code. Replacing all printf
calls with something else is not feasible.)
I feel like this should be easy. There are variations of this question on StackOverflow already, but none that I could find are exactly the same.
I can use fopencookie
to get a FILE*
that ends up calling my callback. So far, so good. The challenge is to get stdout
and printf
to go there.
I can't use freopen
because it takes a string path. The FILE*
I want to redirect to is not a file on the filesystem but rather just exists at runtime.
I can't use dup2
because the FILE*
from fopencookie
does not have a file descriptor (fileno
returns -1).
The glibc documentation suggests that I can simply reassign stdout
to my new FILE*
: "stdin, stdout, and stderr are normal variables which you can set just like any others.". This does almost work. Anything printed with fprintf (stdout, "whatever")
does go to my callback, and so does any printf
that has any format specifiers. However, any call to printf
with a string with no format specifiers at all still goes to the "original" stdout.
How can I achieve what I'm trying to do?
PS: I don't care about portability. This will only ever run on my current environment.
#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;
}