unsuccessful use of popen() in C?
Asked Answered
B

4

4

I can run the following command

xwd -root | xwdtopnm | pnmtojpeg > screen.jpg

in a terminal under linux and it will produce a screenshot of my current screen.

I try to do the following with the code:

#include <stdio.h>
#include <stdlib.h>
int main()
{
   FILE *fpipe;
   char *command="xwd -root | xwdtopnm | pnmtojpeg";
   char line[256];

   if ( !(fpipe = (FILE*)popen(command,"r")) )
   {  // If fpipe is NULL
      perror("Problems with pipe");
      exit(1);
   }

   while ( fgets( line, sizeof line, fpipe))
   {
      //printf("%s", line);
      puts(line);
   }
   pclose(fpipe);
}

then I compile and run the program ./popen > screen.jpg but the resulting file screen.jpg is unrecongizable. How can I do this so that I can pipe through my program correctly?

Bcd answered 8/5, 2009 at 10:35 Comment(0)
S
7

You shouldn't use fgets and puts for dealing with binary data. fgets will stop whenever it sees a newline. Worse, puts will output extra newlines and it will also stop whenever it runs into a \0. Use fread and fwrite instead.

Skiagraph answered 8/5, 2009 at 10:46 Comment(1)
Sorry, I meant fread and fwrite, not read and write. (just edited my answer)Skiagraph
B
2

The functions fgets and puts aren't intended to be used with binary data like image files. They should only be used with strings of text. In C, strings end with a null byte ('\0'). Since that's really just a zero, it might appear anywhere in a binary file. Let's say that line[] is filled with 256 characters of data. When you call puts, the function reads the array until it encounters a null byte then assumes it has reached the end of the string and stops. Since in a binary file a null byte might appear anywhere (and not just at the end of the array), the puts function could easily fail to print out sections of your data.

If I were you, I'd research the fread and fwrite functions and use them instead. On a Linux machine, you should just be able to type man 3 fread to read documentation for both functions.

Balliett answered 8/5, 2009 at 11:2 Comment(0)
B
2

For those having this same problem, I ended up finally getting it working by using the Unix read/write system calls:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

//writes to an output file test.jpg directly
int main()
{
    FILE *fpipe;
    char *command="xset b off  && xwd -root | xwdtopnm 2> /dev/null | pnmtojpeg";
    char buff[256];
    size_t result_write;
    size_t result_read;

    if ( !(fpipe = (FILE*)popen(command,"r")) )
    {  // If fpipe is NULL
        perror("Problems with pipe");
        exit(1);
    }

    int dest_fd = open("test.jpg",  O_RDWR|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR );
    int fd = fileno(fpipe);
    while((result_read = read(fd, buff, sizeof(char)*256))>0){  
        if(result_read < 0){
            perror("Problem while reading.\n");
            exit(1);
        }
        result_write = write(dest_fd, buff, sizeof(char)*256);
        if(result_write < 0){
            perror("Probelms writing to outputfile.\n");
            exit(1);
        }   
    }
    close(dest_fd);     
   pclose(fpipe);
}
Bcd answered 8/5, 2009 at 20:34 Comment(1)
As long as you make sure not to perform buffered IO (fread, fscanf, fwrite, fprintf, ...) and unbuffered IO (read, write, ...) on the same file, this should be fine. Don't understand why you didn't just use fread and fwrite, though.Cascabel
T
0

Without testing your code I hav doubts that "xwd -root | xwdtopnm | pnmtojpeg" works as an argument for a C - Pipe.

I wouldn't use a C program anyway for such a problem. Use a simple Bash script instead.

Tattan answered 8/5, 2009 at 10:42 Comment(1)
I can't use a Bash script, this is merely a test. I need somekind of programmatic way of taking screenshots in X11 and this is the best I could come up with without going through all the X11 source code.Bcd

© 2022 - 2024 — McMap. All rights reserved.