I am writing a utility which accepts either a filename, or reads from stdin.
I would like to know the most robust / fastest way of checking to see if stdin exists (data is being piped to the program) and if so reading that data in. If it doesn’t exist, the processing will take place on the filename given. I have tried using the following the test for size of stdin but I believe since it’s a stream and not an actual file, it’s not working as I suspected it would and it’s always printing -1. I know I could always read the input 1 character at a time while != EOF but I would like a more generic solution so I could end up with either a fd or a FILE* if stdin exists so the rest of the program will function seamlessly. I would also like to be able to know its size, pending the stream has been closed by the previous program.
long getSizeOfInput(FILE *input){
long retvalue = 0;
fseek(input, 0L, SEEK_END);
retvalue = ftell(input);
fseek(input, 0L, SEEK_SET);
return retvalue;
}
int main(int argc, char **argv) {
printf("Size of stdin: %ld\n", getSizeOfInput(stdin));
exit(0);
}
Terminal:
$ echo "hi!" | myprog
Size of stdin: -1
First, ask the program to tell you what is wrong by checking the
errno, which is set on failure, such as duringfseekorftell.Others (tonio & LatinSuD) have explained the mistake with handling stdin versus checking for a filename. Namely, first check
argc(argument count) to see if there are any command line parameters specifiedif (argc > 1), treating-as a special case meaningstdin.If no parameters are specified, then assume input is (going) to come from
stdin, which is a stream not file, and thefseekfunction fails on it.In the case of a stream, where you cannot use file-on-disk oriented library functions (i.e.
fseekandftell), you simply have to count the number of bytes read (including trailing newline characters) until receiving EOF (end-of-file).For usage with large files you could speed it up by using
fgetsto a char array for more efficient reading of the bytes in a (text) file. For a binary file you need to usefopen(const char* filename, "rb")and usefreadinstead offgetc/fgets.You could also check the for
feof(stdin)/ferror(stdin)when using the byte-counting method to detect any errors when reading from a stream.The sample below should be C99 compliant and portable.