In article <fv***********@pc-news.cogsci.ed.ac.uk>
Richard Tobin <ri*****@cogsci.ed.ac.ukwrote:
>... there's a much more elegant solution for
some stdio implementations (I don't know about Windows). These
implementations store pointers to the underlying i/o functions in the
FILE struct, so by modifying them you can arrange for your own
function to be called when the data is flushed.
But these are not exposed to users, for various reasons; you
are only supposed to know about funopen() (and several shorter
names for specific cases; funopen() is the general case).
>For example, MacOS-X's FILE structure (which I think it inherits
from FreeBSD) includes:
/* operations */
void *_cookie; /* cookie passed to io functions */
int (*_close)(void *);
int (*_read) (void *, char *, int);
fpos_t (*_seek) (void *, fpos_t, int);
int (*_write)(void *, const char *, int);
This came from my stdio by way of 4.4BSD and then (presumably)
FreeBSD, yes.
>I would be nice if this was standardised, since it's something that
people have been wanting to do for decades.
This lets you do:
struct whatever some_data_struct;
int read_op(void *, char *, int);
int write_op(void *, const char *, int);
... set up "some_data_struct" as needed ...
fp = funopen(&some_data_struct, read_op, write_op, NULL, NULL);
... handle failure ...
some_library_function(fp); /* uses fprintf() and/or fgets() */
... etc ...
so that you can capture the output of "well-behaved" library
functions that write to a stdio "FILE *", feed input to "well-behaved"
library functions that read from a "FILE *", and so on.
In the OP's case, however, he is calling a "poorly-behaved" library
function that uses a "global variable"[%], namely stdout. Since
stdout need not be a modifiable lvalue, you cannot simply do:
fflush(stdout);
saved_stdout = stdout;
stdout = fp;
bad_library_function();
fflush(stdout);
stdout = saved_stdout;
If you are willing to go for non-portable solutions, the sequence:
target_fd = open_or_pipe_or_whatever();
... check for failures ...
saved_stdout_fd = dup(STDOUT_FILENO);
... check for failures ...
fflush(stdout);
dup2(target_fd, STDOUT_FILENO);
bad_library_function();
fflush(stdout);
dup2(saved_stdout_fd, STDOUT_FILENO);
will do the trick on POSIX systems. The dup2() calls changes the
"global variable"[%] STDOUT_FILENO "behind the back" of the C
library stdio. The pre- and post-call fflush()es make sure that
all stdout output goes to the "correct" fd. The non-portable
version that reassigns the "global variable" stdout uses the same
technique: push out all the "correct" output, make a "global
variable" change, call the badly behaved function with the system's
global state appropriately rearranged, push out *that* output, then
rearrange the global state once more to put it back to normal.
[% The words "global variable" are in quotes because C does not
define the term, and in fact stdout and POSIX_FILENO may well be
constants, not variables. In practice, stdout is either an "address
constant", like &__sstdout, or an actual variable but possibly
const-qualfied, while POSIX_FILENO is simply the integer constant
1. Nonetheless, the POSIX stdout descriptor is a "global variable",
by my definition. It is changeable via system call: dup2(fd, 1)
changes it.]
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out)
http://web.torek.net/torek/index.html