Friday, May 6, 2011

popen() in C

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?

From stackoverflow
  • 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.

  • 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.

    Laurence Gonsalves : Sorry, I meant fread and fwrite, not read and write. (just edited my answer)
  • 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.

  • 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);
    }
    
    ephemient : 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.

0 comments:

Post a Comment