Reputation: 7506
I installed an application and its command line can do:
command -input 1.txt
command < 1.txt
echo "hello" | command
and output something. I don't have the source code and want to implement that behaviour too.
What I've tried is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0){
rewind(stdin);
printf("stdin has data\n");
char buffer[100];
fgets(buffer, sizeof buffer, stdin);
printf("stdin data are: %s\n", buffer);
}else{
if (argc < 2){
printf("no cmd arguments\n");
return -1;
}else{
printf("command line argument: %s\n", argv[1]);
FILE* fp = fopen(argv[1], "r");
if (fp == NULL){
printf("NULL fp pointer\n");
return -1;
}
char a[100] = {0};
fgets(a, sizeof a, fp);
printf("first line of file: %s\n", a);
}
}
return 0;
}
But the problem is that pipes are not seekable. So ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0)
doesn't fit all cases.
One solution that I think of is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if (argc > 1){
//open file with argv[1] as filename
//read data from disk file
}else{
//read data from stdin
if(stdin is file){
//get file size
//read data from stdin
}else if(stdin is pipe){
//get pipe size
//read data from stdin
}
}
return 0;
}
I have 2 problems with this code:
Is there a ispipe()
function which works like isatty(fileno(stdin))
? I need to tell if stdin
is a pipe
.
How do I get the stdin
size/length from a pipe? Apparently I can't use:
fseek(stdin, 0, SEEK_END);
long size = ftell(stdin));
As @Peter pointed out in the comment, I should not try to get the stdin size from a pipe beforehand, then how do I know it reaches the end? Could anyone gives me an minimum example about this "stream-based processing"?
Upvotes: 0
Views: 457
Reputation: 52354
You can use the fstat()
syscall to tell if standard input is a pipe (Either anonymous or named), or a file (And if a file, find its size):
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
struct stat s;
if (fstat(STDIN_FILENO, &s) < 0) {
perror("fstat");
return EXIT_FAILURE;
}
switch (s.st_mode & S_IFMT) {
case S_IFIFO:
puts("standard input is a pipe.");
break;
case S_IFREG:
printf("standard input is a file that is %ld bytes in size.\n",
(long)s.st_size);
break;
case S_IFCHR:
if (isatty(STDIN_FILENO)) {
puts("standard input is a terminal.");
} else {
puts("standard input is a character device.");
}
break;
default:
puts("standard input is something else.");
}
return 0;
}
Example:
$ gcc testpipe.c
$ cat testpipe.c | ./a.out
standard input is a pipe.
$ ./a.out < testpipe.c
standard input is a file that is 525 bytes in size.
$ ./a.out
standard input is a terminal.
Upvotes: 3
Reputation: 1236
The only way to be sure that you won't recieve more data from a pipe is when it is closed (SIGPIPE
signal).
Thus, as stated in comments, allocating/reading the right of memory is challenging with pipes, since they can be infinite (e.g. /dev/random
). You have to make hypothesis or use extra data in order to handle the pipe.
Depending on your use case, these strategies can be one of:
echo -e'\x05\x00\x00\x00Hello'|./myprog
. With that strategy, it is trivial to read the pipe but it requieres that you know the total size of the input before you start sending it.PIPE_MAX_SIZE
bytes or you wait more than TIMEOUT_PIPE
, close the pipe and handle the possibly incomplete message.Upvotes: 2