I want to store the output of a function (matrix_output_printf()) printing the following output (a matrix):
0 1 2
1 2 3
2 3 4
I would like to save this output in a text file.
In a first attempt, I modified the original in matrix_output_fprintf() so that it stores the output continuously using fprintf(). I indeed stores the output but the code of matrix_output_printf() has been changed
However, I would like not to modify matrix_output_printf() as, let's say, it is part of a package and want to test it without modifiying it.
Is there a way to store (using C and not bash) the output of matrix_output_printf() from outside the function (or without using fprintf()?
The content of file.txt is the following:
0 1 2
1 2 3
2 3 4
Here is the code:
/* main.c */
#include <stdio.h>
#include <stdlib.h>
void matrix_output();
void matrix_output_printf(){
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
printf("%d\t", i+j);
}
printf("\n");
}
}
void matrix_output_fprintf(){
FILE * fp;
fp = fopen("file.txt", "w");
fclose(fp);
fp = fopen("file.txt", "ab");
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
fprintf(fp, "%d\t", i+j);
}
fprintf(fp, "\n");
}
fclose(fp);
}
int main ()
{
matrix_output_printf();
matrix_output_fprintf();
return 0;
}
If you want to redirect stdout to a file, you can do that with the freopen function:
freopen("file.txt", "wb", stdout);
After this call, any write to stdout will write to the file "file.txt".
For the sake of completeness, here is the complete working code which worked for me, thanks to the answer of #dbush:
/* main.c */
#include <stdio.h>
#include <stdlib.h>
void matrix_output();
void matrix_output_printf(){
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
printf("%d\t", i+j);
}
printf("\n");
}
}
int main ()
{
freopen("file.txt", "wb", stdout);
matrix_output_printf();
fclose(stdout);
return 0;
}
If you're on a POSIX-compliant system, you can use the dup2 system call.
From the man page:
The dup() system call allocates a new file descriptor that refers
to the same open file description as the descriptor oldfd.
.....The dup2() system call performs the same task as dup(), but
instead of using the lowest-numbered unused file descriptor, it
uses the file descriptor number specified in newfd. In other
words, the file descriptor newfd is adjusted so that it now
refers to the same open file description as oldfd.
Here's an example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#define FILE_NAME "file.txt"
int main (void)
{
int fd = open (FILE_NAME, O_WRONLY | O_CREAT);
if (fd == -1) {
perror ("open()");
return EXIT_FAILURE;
}
/* We want stdout to point to this file */
int rv = 0;
do {
rv = dup2 (fd, STDOUT_FILENO);
} while ((rv == -1) && (errno == EINTR));
if (rv == -1) {
close (fd);
perror ("dup2()");
return EXIT_FAILURE;
}
/* We no longer need the original file descriptor */
close (fd);
/* Now writing to STDOUT_FILENO == FILE.TXT_FILENO */
printf ("You're my sunshine now.\n");
return EXIT_FAILURE;
}
Something to note:
If the file descriptor newfd was previously open, it is closed
before being reused; the close is performed silently (i.e., any
errors during the close are not reported by dup2()).
So we don't need to close STDOUT_FILENO after the call to dup2(). It is done for us automatically.
But the above function is only defined in the POSIX standard, not the C standard. So we can take another route (one already mentioned). You can redirect the stdout to file.txt with freopen.
From the man page:
The freopen() function shall first attempt to flush the stream
associated with stream as if by a call to fflush(stream).
Failure to flush the stream successfully shall be ignored. If
pathname is not a null pointer, freopen() shall close any file
descriptor associated with stream. Failure to close the file
descriptor successfully shall be ignored. The error and end-of-
file indicators for the stream shall be cleared.
The freopen() function shall open the file whose pathname is the
string pointed to by pathname and associate the stream pointed to
by stream with it. The mode argument shall be used just as in
fopen().
The original stream shall be closed regardless of whether the
subsequent open succeeds.
#define FILE_NAME "file.txt"
/* Upon successful completion, freopen() shall return the
* value of stream. Otherwise, a null pointer shall be returned,
* and errno shall be set to indicate the error.
*/
FILE *fp = freopen (FILE_NAME, "w", stdout);
if (!fp) {
/* freopen() failed. Handle error here. */
....
}
Note that this has the disadvantage that there's generally no way to undo it, and as #chqrlie said in the comments: "Passing a FILE * to the function for it to use fprintf() is a much more versatile approach."
Related
So I'm working on the server side of my program right now, and I want to do the following:
1) open a file in read/write mode
2) append a word (WORD) to the end of the file
3) [I believe I have all of this part down already] open a pipe, create a child process, have it read directly from the file (file descriptor), execute a command, and send the result into the write/output of the pipe. The parent process reads from the read/input of the pipe and puts the info into a buffer to send back to the client.
What I'm having trouble with is the appending part. I'm pretty sure it appends to the file (with a newline in between the existing text and my WORD) because when I directly open the text file it's there. But when I try to print it from my buffer, it's not there. I have tried closing the file descriptor after writing and reopening and it's not there. I've tried strcat instead of writing to the file descriptor and it's not there.
#define WORD "WORD"
#define BUFFERLENGTH 512
char buffer[BUFFERLENGTH];
int fileDesc = open (filePath, O_RDWR|O_APPEND, 0660);
if (fileDesc <= 0){
write(clientDesc, ERRORMSG, BUFFERLENGTH);
exit(EXIT_FAILURE);
}
read(fileDesc,buffer,BUFFERLENGTH);
long length = lseek(fileDesc,0,SEEK_END);
int status = write(fileDesc,WORD,sizeof(WORD)-1);
read(fileDesc, buffer, BUFFER_LEN+1);
printf("new text: %s\n", buffer); //WORD does not show up at the end of file as intended
Is there something I'm really misunderstanding?
Perhaps I don't fully understand how open(), read(), write(), and lseek() work, but if anyone could help explain to me why this isn't working as intended that'd be greatly appreciated. I've been struggling with this for the past week and the number of tabs I currently have open to searching for a solution is tragic.
After your write() call you're going to be at the end of the file, so read() isn't going to be able to read anything. You'll need to lseek() to a point earlier in the file if you want to be able to read anything from it.
You should be checking the return from read() (and almost all other system calls, for that matter) and use perror() or similar in the case of error, and this will do wonders for helping you to understand what's going on when you see behavior you don't expect.
Modifying your program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define WORD "WORD"
#define BUFFERLENGTH 512
int main(void)
{
char * filePath = "testfile.txt";
char buffer[BUFFERLENGTH] = {0};
// Open file.
int fd = open(filePath, O_RDWR | O_APPEND, 0660);
if (fd < 0) {
perror("couldn't open file");
return EXIT_FAILURE;
}
// Write word to end.
int status = write(fd, WORD, strlen(WORD));
if ( status < 0 ) {
perror("couldn't write");
return EXIT_FAILURE;
}
// Seek to start of file.
long length = lseek(fd, 0, SEEK_SET);
if ( length < 0 ) {
perror("couldn't lseek");
return EXIT_FAILURE;
}
// Read contents of file.
status = read(fd, buffer, BUFFERLENGTH - 1);
if ( status < 0 ) {
perror("couldn't read");
return EXIT_FAILURE;
}
// Print buffer.
printf("file contents: %s\n", buffer);
return 0;
}
yields:
paul#mac:scratch$ touch testfile.txt
paul#mac:scratch$ ./file
file contents: WORD
paul#mac:scratch$ ./file
file contents: WORDWORD
paul#mac:scratch$ ./file
file contents: WORDWORDWORD
paul#mac:scratch$ ./file
file contents: WORDWORDWORDWORD
paul#mac:scratch$
If you want to actually see only the new contents, then you'll need to lseek() to some point other than the start of the file. Since a successful write() will return the number of bytes written, you can use this value to offset back from the end of the file:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define BUFFERLENGTH 512
int main(int argc, char * argv[])
{
if ( argc < 2 ) {
fprintf(stderr, "you need to enter a word argument\n");
return EXIT_FAILURE;
}
char * filePath = "testfile.txt";
char buffer[BUFFERLENGTH] = {0};
// Open file.
int fd = open(filePath, O_RDWR | O_APPEND, 0660);
if ( fd < 0 ) {
perror("couldn't open file");
return EXIT_FAILURE;
}
// Write word to end.
int status = write(fd, argv[1], strlen(argv[1]));
if ( status < 0 ) {
perror("couldn't write");
return EXIT_FAILURE;
}
// Seek to point before last write.
long length = lseek(fd, -status, SEEK_END);
if ( length < 0 ) {
perror("couldn't lseek");
return EXIT_FAILURE;
}
// Read from there to end of file.
status = read(fd, buffer, BUFFERLENGTH - 1);
if ( status < 0 ) {
perror("couldn't read");
return EXIT_FAILURE;
}
// Print buffer.
printf("new text: %s\n", buffer);
return 0;
}
yielding:
paul#mac:scratch$ rm testfile.txt
paul#mac:scratch$ touch testfile.txt
paul#mac:scratch$ ./file2 these
new text: these
paul#mac:scratch$ ./file2 are
new text: are
paul#mac:scratch$ ./file2 some
new text: some
paul#mac:scratch$ ./file2 words
new text: words
paul#mac:scratch$ cat testfile.txt
thesearesomewordspaul#mac:scratch$
I am using C file IO to read value from a sysfs interface in linux. Path and sample value of the register is as follows:
cat /sys/class/powercap/intel-rapl/intel-rapl\:0/energy_uj
56039694184
Code: Added \ after intel-rapl\ to take into account unknown escape sequence
#define FILE_SIZE 512
static FILE *fp;
char filename[FILE_SIZE];
char TEMP[FILE_SIZE];
int FILE, READ;
long int POWER;
FILE = open("/sys/class/powercap/intel-rapl/intel-rapl\\:0/energy_uj", O_RDONLY);
READ = read(FILE, TEMP, sizeof(TEMP));
POWER= strtod(TEMP,NULL);
close(FILE);
sprintf(filename,"test.csv");
fp = fopen(filename,"a+");
fprintf(fp,"\n");
fprintf(fp, "%ld", POWER);
The code compiles without any error, but in the output file I am getting value as 0. Is this due to how I am taking into account the escape sequence?
Thanks.
Since the sysfs files, while 'files' in one sense, may also be nodes, etc.. and not traditional text files, it is often best to let the shell interact with the sysfs files and simply read the needed values from a pipe following a call to popen using the shell command, e.g.
#include <stdio.h>
int main (void) {
long unsigned energy_uj = 0;
FILE *proc = popen (
"cat /sys/class/powercap/intel-rapl/intel-rapl\\:0/energy_uj", "r");
if (!proc) { /* validate pipe open for reading */
fprintf (stderr, "error: process open failed.\n");
return 1;
}
if (fscanf (proc, "%lu", &energy_uj) == 1) /* read/validate value */
printf ("energy_uj: %lu\n", energy_uj);
pclose (proc);
return 0;
}
Example Use/Output
$ ./bin/sysfs_energy_uj
energy_uj: 29378726782
That's not to say you cannot read from the sysfs files directly, but if you have any problems, then reading from a pipe is fine. For the energy_uj value, it can be read directly without issue:
#include <stdio.h>
int main (void) {
long unsigned energy_uj = 0;
FILE *fp = fopen (
"/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj", "r");
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed.\n");
return 1;
}
if (fscanf (fp, "%lu", &energy_uj) == 1) /* read/validate value */
printf ("energy_uj: %lu\n", energy_uj);
fclose (fp);
return 0;
}
Example Use/Output
$ ./bin/sysfs_energy_uj_file
energy_uj: 33636394660
My program(below) writes(with pwrite()) text to a file and reads(with pread()) from the file. My problems are pread function doesn't read my text from file and what's wrong with close function(last part of program)? Results is in the second part. Where is my mistake?
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
int fd, nr, nr2, nw, nw2;
char fl_nm[]={"file_io/pwrite.txt"};
char buf_wr[]={"hello everyone this is first text\n"};
char buf_wr2[]={"Did you miss me? Don't afraid\n"};
char buf_rd[120];
char buf_rd2[120];
//open file
fd = open(fl_nm, O_RDWR|O_CREAT, 0777);
nw = pwrite(fd, &buf_wr, strlen(buf_wr), 14);
//error checking
if(fd == -1){
perror("[error in open]\n");
}
else if(nw == -1){
perror("[error in write]\n");
}
else{
/*if open and write process are okey, read first write data
* from file*/
nr = read(fd, &buf_rd, sizeof(buf_rd));
//display succeeded message about first write and open process
printf("[file is opened]\n");
printf("[succeeded write(1) process]\n");
//read process error control
if(nr == -1){
perror("[error in read]\n");
} else{
printf("[reading(1) data] from %s\n", fl_nm);
printf("[%s]\n", buf_rd);
}
}
//second write process.
nw2= pwrite(fd, &buf_wr2, strlen(buf_wr2), 30);
//write error checking
if(nw2 == -1){
perror("[error in write 2]\n");
}else{
/*if write process is correct
* second read process*/
nr2 = read(fd, &buf_rd2, sizeof(buf_rd));
printf("-----------------------------------\n");
printf("[succeeded write(2) process]\n");
printf("[reading(2) data] from %s\n", fl_nm);
printf("[%s]\n", buf_rd2);
}
//close file
close(fd);
//error checking for close process
if(close(fd) == -1){
perror("[error in close]\n");
}else{
printf("[succeeded in close]\n");
}
return 0;
}
Result:
$ gcc pwrite.c -o pwrite
$ ./pwrite
[file is opened]
[succeeded write(1) process]
[reading(1) data] from file_io/pwrite.txt
[]
-----------------------------------
[succeeded write(2) process]
[reading(2) data] from file_io/pwrite.txt
[]
[error in close]
: Bad file descriptor
1) close() fails because you are closing the file twice:
//close file
close(fd);
//error check close process
if(close(fd) == -1){
After the first call to close(fd);, fd becomes indeterminate and the second call to close(fd) fails. You just need to remove the first call to close(fd);.
2) You are printing buf_rd as if it's a C-string. read() doesn't terminate buf_rd with a null byte.
3) You are writing at random offsets (14 and 30) using pwrite(). But read() reads from current offset - which means the starting byte could a null byte and thus %s stops printing right away (i.e. prints nothing). You are reading a lot more than what you write. That means read() is going to return less than the requested number of bytes. So use the return value of read() to get the number of bytes successfully read.
Instead, print each byte using a loop:
for (size_t l = 0; l < nr; l++)
printf("%c", buf_rd[l]);
and
for (size_t l = 0; l < nr2; l++)
printf("%c", buf_rd2[l]);
You're using pointers incorrectly, accessing to address of arrays should be with their names alone, not &name.
replace &buf_wr with buf_wr, accessing to incorrect address with &buf_wr to writing there will corrupt your stack and also variables defined inside stack
Edit:
replace
nw = pwrite(fd, &buf_wr, strlen(buf_wr), 14);
to
nw = pwrite(fd, buf_wr, strlen(buf_wr), 14);
and all other instances..
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 7 years ago.
Improve this question
My professor asked me to write a simple C program, then asked me to convert using Unix system calls. I have try changing the values around but nothing is working.
Requirement:
Write a new C program newcat, which performs exactly as oldcat, but uses the following UNIX system calls for I/O.
int read(int fd, char *buf, int n);
int write(int fd, char *buf, int n);
int open(char *name, int accessmode, int permission);
int close(int fd);
To open a file for read, you can use the symbolic constant O_RDONLY defined in fcntl.h header file to specify the accessmode. Simply pass 0 for permission. That is, the code will appear as follows:
fd = open (filename, O_RDONLY, 0);
You will need the following header files: sys/types.h, unistd.h and fcntl.h
#include <stdio.h>
/* oldcat: Concatenate files */
int main(int argc, char *argv[])
{
void filecopy(FILE *, FILE *); /* prototype for function */
int fd = open(*fp, O_RDONLy,0)
char *prog = argv[0]; /* program name for errors */
if (argc == 1) /* no args; copy standard input */
filecopy(0, 1);
else
while (--argc > 0)
if (fd == -1) {
fprintf(stderr, "%s: can't open %s\n", prog, *argv);
return(-1);
} else {
filecopy(fp, 1);
fclose(fp);
}
return(0);
}
/* filecopy: copy file ifp to ofp */
void filecopy(FILE *ifp, FILE *ofp)
{
int c;
while ((c = getc(ifp)) != EOF)
putc(c, ofp);
}
Is this the write idea? It still won't compile:
#include <stdio.h>
/* oldcat: Concatenate files */
int main(int argc, char *argv[])
{
void filecopy(int ifp, int ifo);
int fd = open(*File,O_RDONLY,0); //is this correct?
char *prog = argv[0];
if (argc == 1) /* no args; copy standard input */
filecopy(0, 1); //is this correct?
else
while (--argc > 0)
if ((fd == -1) //is this correct?{
fprintf(stderr, "%s: can't open %s\n", prog, *argv);
return(-1);
} else {
filecopy(*FILE, 1);//is this correct?
close(*FILE);//is this correct?
}
return(0);
}
/* filecopy: copy file ifp to ofp */
void filecopy(FILE *ifp, FILE *ofp)//NO CLUE HOW THIS SHOULD BE
{
int c;
while (c = read(fd ,&something,1)//What is &ch/&something?
putc(c, ofp);
}
Assuming your oldcat uses the C standard library calls (like fopen), it's a simple matter of mapping those to the UNIX calls.
At a high level:
fopen -> open
fread -> read
fwrite -> write
fclose -> close
For example, when opening your input file with:
FILE *fIn = fopen ("jargon.txt", "r");
you could instead use:
int inFd = open ("jargon.txt", O_RDONLY, 0);
The other calls are very similar, with similar functionality at the C standard library and UNIX system call levels. Details on those calls can usually be obtained from the manpages by entering something like man 2 open into your shell, or by plugging man open into your favourite search engine.
The only "tricky" mapping is if you've used getchar/putchar-style calls to do the actual reading and writing but that too becomes easy when you realise that (for example) reading a character is functionally identical to reading a block of size one:
int c = getc (fIn);
or:
char c;
int numread = read (inFd, &c, 1);
For your added question:
So to open a file: if (fd = open (fp, O_RDONLY, 0); ) == NULL)
Not quite. The fopen function returns NULL on error because it returns a pointer to a FILE structure.
The lower level calls use file descriptors rather than file handles, the former being a small integer value. So, instead of:
FILE *fp = fopen ("nosuchfile", "r");
if (fp == NULL) doSomethingIntelligent();
you would do something like:
int fd = open ("nosuchfile", O_RDONLY, 0);
if (fd == -1) doSomethingIntelligentUsing (errno);
In terms of what you need to change, the following comes off the top of my head (so may not be totally exhaustive but should be a very good start):
Add the required headers.
Stop using FILE* totally, using int instead.
Translate the fopen/fclose calls to open/close. This includes the function name, different parameters and different return types.
Modify filecopy to use file descriptors rather than file handles.
use 1 instead of stdout when calling filecopy (the latter is a FILE *).
As an example of how to do this, the following program testprog.c will read itself and echo each character to standard output:
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
int main (void) {
int num, ch, inFd;
// Open as read only.
inFd = open ("testprog.c", O_RDONLY, 0);
if (inFd == -1)
printf ("\n**Error %d opening file\n", errno);
// Get and output esach char until EOF/error.
while ((num = read (inFd, &ch, 1) != 0) == 1)
putchar (ch);
// Detect error.
if (num != 0)
printf ("\n**Error %d reading file\n", errno);
// Close file and exit.
close (inFd);
return 0;
}
Please note that documentation of linux sys calls is present in manual called man pages which you can access by using man command in bash shell in a linux system. As UNIX and Linux are quite similar (maybe equivalent) for the syscalls you are interested in you can check the man page for those syscalls in Linux.
All the four read, write, open and close linux syscalls are explained in man pages. You can access the manual page for these syscalls by typing below commands in shell:
man 2 read
man 2 write
man 2 open
man 2 close
These should probably guide you to right direction.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
/* newcat: Concatenate files */
int main(int argc, char *argv[])
{
void filecopy(int ifp, int ofp); /* prototype for function */
int fd;
char *prog = argv[0]; /* program name for errors */
if (argc == 1) /* no args; copy standard input */
filecopy(0,1);
else
while (--argc > 0)
fd = open(*++argv , O_RDONLY,0);
if ( fd == -1) {
fprintf(stderr, "%s: can't open %s\n", prog, *argv);
return(-1);
} else {
filecopy(fd, 1);
close(fd);
}
return(0);
}
/* filecopy: copy file ifp to ofp */
void filecopy(int ifp, int ofp)
{
int c;
while (read(ifp,&c,ofp ) != 0)
write(ofp,&c,ofp);
}
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
You're thinking it wrong.
What you are trying to do:
If stdin exists use it, else check whether the user supplied a filename.
What you should be doing instead:
If the user supplies a filename, then use the filename. Else use stdin.
You cannot know the total length of an incoming stream unless you read it all and keep it buffered. You just cannot seek backwards into pipes. This is a limitation of how pipes work. Pipes are not suitable for all tasks and sometimes intermediate files are required.
First, ask the program to tell you what is wrong by checking the errno, which is set on failure, such as during fseek or ftell.
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 specified if (argc > 1), treating - as a special case meaning stdin.
If no parameters are specified, then assume input is (going) to come from stdin, which is a stream not file, and the fseek function fails on it.
In the case of a stream, where you cannot use file-on-disk oriented library functions (i.e. fseek and ftell), 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 fgets to a char array for more efficient reading of the bytes in a (text) file. For a binary file you need to use fopen(const char* filename, "rb") and use fread instead of fgetc/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.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
long getSizeOfInput(FILE *input){
long retvalue = 0;
int c;
if (input != stdin) {
if (-1 == fseek(input, 0L, SEEK_END)) {
fprintf(stderr, "Error seek end: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (-1 == (retvalue = ftell(input))) {
fprintf(stderr, "ftell failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (-1 == fseek(input, 0L, SEEK_SET)) {
fprintf(stderr, "Error seek start: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
} else {
/* for stdin, we need to read in the entire stream until EOF */
while (EOF != (c = fgetc(input))) {
retvalue++;
}
}
return retvalue;
}
int main(int argc, char **argv) {
FILE *input;
if (argc > 1) {
if(!strcmp(argv[1],"-")) {
input = stdin;
} else {
input = fopen(argv[1],"r");
if (NULL == input) {
fprintf(stderr, "Unable to open '%s': %s\n",
argv[1], strerror(errno));
exit(EXIT_FAILURE);
}
}
} else {
input = stdin;
}
printf("Size of file: %ld\n", getSizeOfInput(input));
return EXIT_SUCCESS;
}
You may want to look at how this is done in the cat utility, for example.
See code here.
If there is no filename as argument, or it is "-", then stdin is used for input.
stdin will be there, even if no data is pushed to it (but then, your read call may wait forever).
You can just read from stdin unless the user supply a filename ?
If not, treat the special "filename" - as meaning "read from stdin". The user would have to start the program like cat file | myprogram - if he wants to pipe data to it, and myprogam file if he wants it to read from a file.
int main(int argc,char *argv[] ) {
FILE *input;
if(argc != 2) {
usage();
return 1;
}
if(!strcmp(argv[1],"-")) {
input = stdin;
} else {
input = fopen(argv[1],"rb");
//check for errors
}
If you're on *nix, you can check whether stdin is a fifo:
struct stat st_info;
if(fstat(0,&st_info) != 0)
//error
}
if(S_ISFIFO(st_info.st_mode)) {
//stdin is a pipe
}
Though that won't handle the user doing myprogram <file
You can also check if stdin is a terminal/console
if(isatty(0)) {
//stdin is a terminal
}
Just testing for end of file with feof would do, I think.
Note that what you want is to know if stdin is connected to a terminal or not, not if it exists. It always exists but when you use the shell to pipe something into it or read a file, it is not connected to a terminal.
You can check that a file descriptor is connected to a terminal via the termios.h functions:
#include <termios.h>
#include <stdbool.h>
bool stdin_is_a_pipe(void)
{
struct termios t;
return (tcgetattr(STDIN_FILENO, &t) < 0);
}
This will try to fetch the terminal attributes of stdin. If it is not connected to a pipe, it is attached to a tty and the tcgetattr function call will succeed. In order to detect a pipe, we check for tcgetattr failure.