Reputation: 1218
I've implemented my own message logging functions for my C command line program, I want to be able to print Info messages to stdout
, Error messages to stderr
, and Warning messages to both without having duplicate messages if they output to the same location.
The Info and Error messages work fine, but for the Warning messages I have no idea how to efficiently check if stdout and stderr file streams point to the same output location. My code works, but I can't figure out why, because logically when stepping through the function it should produce duplicate entrys if stdout
and stderr
point to the same file.
I've checked when stdout
and stderr
have the same output file, they still produce different memory addresses in the pointers and fileno(stdout)
and fileno(stderr)
are different.
tl;dr I have code that works, but as far as I'm aware... it shouldn't. Can anyone help explain why it is working or does anyone know the correct way to solve this.
Edit: the way I'm calling the program is: myProgram >out.lis 2>out.lis
Edit 2: When calling like this it does produce duplicates: myProgram >out.lis 2>&1
My Code for the warning message:
/* Warning message, a unified function for printing warning messages */
/* Warning messages are printed to both the output log and the error log (if different) */
void warningMessage(char * msg) {
if (isatty(fileno(stderr))) {
/* Add color if printing to terminal */
fprintf(stderr, "\033[0;31;43mWarning: \033[0;30;43m%s\033[39;49m\r\n", msg);
} else {
fprintf(stderr, "\nWarning: %s\n", msg);
if (isatty(fileno(stdout))) {
fprintf(stdout, "\033[0;31;43mWarning: \033[0;30;43m%s\033[39;49m\r\n", msg);
} else {
fprintf(stdout, "\nWarning: %s\n", msg);
}
}
}
Any other pointers about my code would be helpful as well! I've only recently started learning C!
Upvotes: 1
Views: 610
Reputation: 690
The trick is going to be fstat().
Get the fileno() for both (should be 1 and 2 respectively, but might differ for non-Unix OSes), pass these as the first parameter to fstat(), and compare the structures filled in as the second parameter. I would expect exact matches if they output to the same place. I could believe that might the timestamps might be different.
I'm afraid I can't tell you if MS-Windows has the same call or not, but it should have an equivalent.
Don't forget to flush appropriately.
The answer by Some programmer dude notes you only need to check two fields. This is correct (except perhaps on some weird filesystems).
There are some weird things that can happen. If there are two device nodes for the same device (as in /dev/tty1 and /dev/tty1_alternate pointing to the same device) then st_ino will not match but st_rdev will. I would treat these as different, as the user is playing games with you.
It might also be good to try to check if the two opens are the same open.
(Dealing with the myprogram >out 2>out
case.)
For this, you probably need to mess with some of the parameters and see if changing one changes the other. Probably the fcntl() function, using F_GETFL and F_SETFL.
Upvotes: 2
Reputation: 409176
A mentioned in the answer by David G. you could use fstat
to check where the file descriptors really write.
On a POSIX system you use the stat
structure members st_dev
and st_ino
to find out what files are used. If these two members are equal for both stdout
and stderr
then you're writing to the same file.
I also suggest you make this check only once early in your program. Redirection happens only once, and you don't need to check for it every time you want to write a message.
Upvotes: 1