tags:

views:

551

answers:

5

I have a C program, and I'd like to have it filter all its input with tr. So, I'd like to start up tr as a child process, redirect my stdin to it, then capture tr's stdout and read from that.

Edit: here's the code I have so far, which doesn't work. It segfaults instantly, but I don't understand why:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
  int ch;
  int fd = stripNewlines();

  while((ch = getc(fd)) != EOF){
    putc(ch, stdout);
  }

  return 0;
}

int stripNewlines(){
  int fd[2], ch;
  pipe(fd);

  if(!fork()){
    close(fd[0]);

    while((ch = getc(stdin)) != EOF){
      if(ch == '\n'){ continue; }
      putc(ch, fd[1]);
    }

    exit(0);
  }else{
    close(fd[1]);

    return fd[0];
  }
}

Edit: Turns out this was two things: one was that my header didn't define stdin and stdout as 0 and 1, so I was actually reading/writing to totally random pipes. The other is that for some reason getc and putc don't work how I'd expect, so I had to use read() and write() instead. If I do that, it's perfect:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char** argv){
  int ch;
  int fd = stripNewlines();

  while(read(fd, &ch, 1) == 1){
    write(1, &ch, 1);
  }

  return 0;
}

int stripNewlines(){
  int fd[2];
  int ch;
  pipe(fd);

  if(!fork()){
    close(fd[0]);

    while(read(0, &ch, 1) == 1){
      if(ch == '\n'){ continue; }
      write(fd[1], &ch, 1);
    }

    exit(0);
  }else{
    close(fd[1]);
    return fd[0];
  }
}
A: 

Any reason why you just can't pipe the input from tr to your program?

tr A-Z a-z | myprogram

Jon
Yeah, but it's kind of complicated to go into. I want to handle all this inside the program itself.
Ross Andrews
+4  A: 

Here is something to get you started

http://beej.us/guide/bgipc/output/html/multipage/pipes.html

Tom
I see from that how to do everything except send my stdin to the child process... Is there some way in exec to tell it what the child process' stdin file descriptor should be?
Ross Andrews
@Ross: The child inherits the parent's stdin at the time of `fork()`. So you need to have the parent call `pipe()`, then `fork()`, then `dup2(pipe[0], STDIN_FILENO)`, then `close(pipe[0]); close (pipe[1]);`, in that order. The child calls `dup2(pipe[1], STDOUT_FILENO)`, `close(pipe[0]); close (pipe[1]);`, then `exec()`.
caf
+1  A: 

Reading it from stdin makes life more difficult. If you can live with reading from another FILE *, it's pretty easy to use popen() to spawn tr, and read from the FILE * it returns.

Edit: if you can't do that, then you need to get into a bit of ugliness. Start with using popen to spawn tr with its output redirected. Then use fileno to get the file numbers associated with that FILE * and with stdin. Finally, use dup2 to associate stdin's file descriptor with the pipe from tr.

Jerry Coffin
Yep, it makes it difficult enough that I had to ask the question. If I could do that, I would. :-)
Ross Andrews
A: 
#include <stdio.h>
int main()
{
    char x1[100];
    scanf("%[^\n]",x1); // read entire line from tr i.e., from stdin
    printf("\n%s",x1);
}

and use

tr A-Z a-z | myprogram

Vineel Kumar Reddy
A: 

See popen(3). Basically all you need to do is

FILE *in = popen("tr <args>", "r");

and then read from in.

Chris Dodd