Redirect printf to fopencookie
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 bytesn", 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 stdoutn");
printf ("Hello world, this is a printf with a digit: %dn", 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;
}
c printf raspbian glibc stdio
add a comment |
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 bytesn", 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 stdoutn");
printf ("Hello world, this is a printf with a digit: %dn", 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;
}
c printf raspbian glibc stdio
1
Sounds like an issue with the glibc printf extension. Have you considered just using#define printf cookie_printf
or something to that effect. It's pretty easy to write your own function that has the same signature asprintf
that then callsvfprintf
. stackoverflow.com/a/1737675/1028434
– Fred
Dec 30 '18 at 18:47
add a comment |
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 bytesn", 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 stdoutn");
printf ("Hello world, this is a printf with a digit: %dn", 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;
}
c printf raspbian glibc stdio
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 bytesn", 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 stdoutn");
printf ("Hello world, this is a printf with a digit: %dn", 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;
}
c printf raspbian glibc stdio
c printf raspbian glibc stdio
asked Dec 30 '18 at 18:03
peterpipeterpi
385111
385111
1
Sounds like an issue with the glibc printf extension. Have you considered just using#define printf cookie_printf
or something to that effect. It's pretty easy to write your own function that has the same signature asprintf
that then callsvfprintf
. stackoverflow.com/a/1737675/1028434
– Fred
Dec 30 '18 at 18:47
add a comment |
1
Sounds like an issue with the glibc printf extension. Have you considered just using#define printf cookie_printf
or something to that effect. It's pretty easy to write your own function that has the same signature asprintf
that then callsvfprintf
. stackoverflow.com/a/1737675/1028434
– Fred
Dec 30 '18 at 18:47
1
1
Sounds like an issue with the glibc printf extension. Have you considered just using
#define printf cookie_printf
or something to that effect. It's pretty easy to write your own function that has the same signature as printf
that then calls vfprintf
. stackoverflow.com/a/1737675/1028434– Fred
Dec 30 '18 at 18:47
Sounds like an issue with the glibc printf extension. Have you considered just using
#define printf cookie_printf
or something to that effect. It's pretty easy to write your own function that has the same signature as printf
that then calls vfprintf
. stackoverflow.com/a/1737675/1028434– Fred
Dec 30 '18 at 18:47
add a comment |
1 Answer
1
active
oldest
votes
This appears to be a bug in GLIBC.
The reason that printf("simple string")
works differently from printf("foo %d", 123)
is that GCC transforms the former into a puts
, with the notion that they are equivalent.
As far as I can tell, they should be equivalent. This man page states that puts
outputs to stdout
, just like printf
does.
However, in GLIBC printf
outputs to stdout
here, but puts
outputs to _IO_stdout
here, and these are not equivalent. This has already been reported as a glibc bug (upstream bug).
To work around this bug, you could build with -fno-builtin-printf
flag. That prevents GCC from transforming printf
into puts
, and on my system produces:
$ ./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.
"
This workaround is of course incomplete: if you call puts
directly, or link in object files that call printf("simple string")
and were not compiled with -fno-builtin-printf
(perhaps from 3rd-party library), then you'll still have a problem.
Unfortunately you can't assign to _IO_stdout
(which is a macro). The only other thing you could do (that I can think of) is link in your own puts
, which just returns printf("%s", arg)
. That should work if you are linking against libc.so.6
, but may cause trouble if you link against libc.a
.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53980112%2fredirect-printf-to-fopencookie%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
This appears to be a bug in GLIBC.
The reason that printf("simple string")
works differently from printf("foo %d", 123)
is that GCC transforms the former into a puts
, with the notion that they are equivalent.
As far as I can tell, they should be equivalent. This man page states that puts
outputs to stdout
, just like printf
does.
However, in GLIBC printf
outputs to stdout
here, but puts
outputs to _IO_stdout
here, and these are not equivalent. This has already been reported as a glibc bug (upstream bug).
To work around this bug, you could build with -fno-builtin-printf
flag. That prevents GCC from transforming printf
into puts
, and on my system produces:
$ ./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.
"
This workaround is of course incomplete: if you call puts
directly, or link in object files that call printf("simple string")
and were not compiled with -fno-builtin-printf
(perhaps from 3rd-party library), then you'll still have a problem.
Unfortunately you can't assign to _IO_stdout
(which is a macro). The only other thing you could do (that I can think of) is link in your own puts
, which just returns printf("%s", arg)
. That should work if you are linking against libc.so.6
, but may cause trouble if you link against libc.a
.
add a comment |
This appears to be a bug in GLIBC.
The reason that printf("simple string")
works differently from printf("foo %d", 123)
is that GCC transforms the former into a puts
, with the notion that they are equivalent.
As far as I can tell, they should be equivalent. This man page states that puts
outputs to stdout
, just like printf
does.
However, in GLIBC printf
outputs to stdout
here, but puts
outputs to _IO_stdout
here, and these are not equivalent. This has already been reported as a glibc bug (upstream bug).
To work around this bug, you could build with -fno-builtin-printf
flag. That prevents GCC from transforming printf
into puts
, and on my system produces:
$ ./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.
"
This workaround is of course incomplete: if you call puts
directly, or link in object files that call printf("simple string")
and were not compiled with -fno-builtin-printf
(perhaps from 3rd-party library), then you'll still have a problem.
Unfortunately you can't assign to _IO_stdout
(which is a macro). The only other thing you could do (that I can think of) is link in your own puts
, which just returns printf("%s", arg)
. That should work if you are linking against libc.so.6
, but may cause trouble if you link against libc.a
.
add a comment |
This appears to be a bug in GLIBC.
The reason that printf("simple string")
works differently from printf("foo %d", 123)
is that GCC transforms the former into a puts
, with the notion that they are equivalent.
As far as I can tell, they should be equivalent. This man page states that puts
outputs to stdout
, just like printf
does.
However, in GLIBC printf
outputs to stdout
here, but puts
outputs to _IO_stdout
here, and these are not equivalent. This has already been reported as a glibc bug (upstream bug).
To work around this bug, you could build with -fno-builtin-printf
flag. That prevents GCC from transforming printf
into puts
, and on my system produces:
$ ./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.
"
This workaround is of course incomplete: if you call puts
directly, or link in object files that call printf("simple string")
and were not compiled with -fno-builtin-printf
(perhaps from 3rd-party library), then you'll still have a problem.
Unfortunately you can't assign to _IO_stdout
(which is a macro). The only other thing you could do (that I can think of) is link in your own puts
, which just returns printf("%s", arg)
. That should work if you are linking against libc.so.6
, but may cause trouble if you link against libc.a
.
This appears to be a bug in GLIBC.
The reason that printf("simple string")
works differently from printf("foo %d", 123)
is that GCC transforms the former into a puts
, with the notion that they are equivalent.
As far as I can tell, they should be equivalent. This man page states that puts
outputs to stdout
, just like printf
does.
However, in GLIBC printf
outputs to stdout
here, but puts
outputs to _IO_stdout
here, and these are not equivalent. This has already been reported as a glibc bug (upstream bug).
To work around this bug, you could build with -fno-builtin-printf
flag. That prevents GCC from transforming printf
into puts
, and on my system produces:
$ ./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.
"
This workaround is of course incomplete: if you call puts
directly, or link in object files that call printf("simple string")
and were not compiled with -fno-builtin-printf
(perhaps from 3rd-party library), then you'll still have a problem.
Unfortunately you can't assign to _IO_stdout
(which is a macro). The only other thing you could do (that I can think of) is link in your own puts
, which just returns printf("%s", arg)
. That should work if you are linking against libc.so.6
, but may cause trouble if you link against libc.a
.
edited Jan 1 at 8:24
Florian Weimer
16.9k31148
16.9k31148
answered Dec 30 '18 at 21:41
Employed RussianEmployed Russian
126k20169239
126k20169239
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53980112%2fredirect-printf-to-fopencookie%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Sounds like an issue with the glibc printf extension. Have you considered just using
#define printf cookie_printf
or something to that effect. It's pretty easy to write your own function that has the same signature asprintf
that then callsvfprintf
. stackoverflow.com/a/1737675/1028434– Fred
Dec 30 '18 at 18:47