3

C language is used. I have a function that writes to stdout.

I would like to capture that output, modify it a bit (replacing some strings). And than output it again to the stdout. So I want to start with:

char huge_string_buf[MASSIVE_SIZE];
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */

The question is now, how do I output the huge_string_buffer back to the original stdout?

5 Answers 5

2

One idea is to mimic the functionality of the standard Unix utility tee, but to do so entirely within your program, without relying on outside redirection.

So I've written a simple function, mytee(), which seems to work. It uses shmget(), pipe(), fork(), and dup2():

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>

static char *mytee(int size) {
   int shmid = shmget(IPC_PRIVATE, size + 1, 0660 | IPC_CREAT);
   int pipe_fds[2];
   pipe(pipe_fds);

   switch (fork()) {
      case -1:                      // = error
         perror("fork");
         exit(EXIT_FAILURE);
      case 0: {                     // = child
         char *out = shmat(shmid, 0, 0), c;
         int i = 0;
         out[0] = 0;
         dup2(pipe_fds[0], 0);      // redirect pipe to child's stdin
         setvbuf(stdout, 0, _IONBF, 0);
         while (read(0, &c, 1) == 1 && i < size) {
            printf("<%c>", c);      // pass parent's stdout to real stdout,
            out[i++] = c;           // and then buffer in mycapture buffer
            out[i] = 0;             // (the extra <> are just for clarity)
         }
         _exit(EXIT_SUCCESS);
      }
      default:                      // = parent
         dup2(pipe_fds[1], 1);      // replace stdout with output to child
         setvbuf(stdout, 0, _IONBF, 0);
         return shmat(shmid, 0, 0); // return the child's capture buffer
   }
}

My test program is:

int main(void) {
   char *mycapture = mytee(100);    // capture first 100 bytes
   printf("Hello World");           // sample test string
   sleep(1);
   fprintf(stderr, "\nCaptured: <%s>\n", mycapture);
   return 0;
}

The output is:

<H><e><l><l><o>< ><W><o><r><l><d>
Captured: <Hello World>

To use this in your application, in mytee() you'll need to replace the test statement printf("<%c>", c) with just write(1, &c, 1). And you may need to handle signals in the call to read. And after each of the two dup2()'s, you may want to add:

  close(pipe_fds[0]);
  close(pipe_fds[1]);

For a reference on this sort of stuff, see for example the excellent and short 27-year-old 220-page O'Reilly book Using C on the Unix System by Dave Curry.

Sign up to request clarification or add additional context in comments.

Comments

1

The Unix way to do this is really to just write a little program that does the input processing you need, and then pipe the output of that other program to it on the command line.

If you insist on keeping it all in your C program, what I'd do instead is rewrite that function to have it send its output to a given char buffer (preferably returning the buffer's char *), so that it can be sent to stdout or processed as the client desires.

For example, the old way:

void usage () {
    printf ("usage: frob sourcefile [-options]\n");
}

...and the new way:

char * usage(char * buffer) {
    strcpy (buffer, "usage: frob sourcefile [-options]\n");
    return buffer;
}

4 Comments

i do not have the source of the funciton, it is called from a .so
@Stasik have you got the solution? I have the same problem, I run ipmitool to get some information, and want to modify the content then output to stdout. I have the code of ipmitool, but I do not want to change the code too much.
I ended wrapping it up in scripts and doing text processing there
@Stasik - I'd suggest writing that up in an answer and accepting it. I think the statute of limitations on answering your own question being sketchy has long passed, and it might help someone else like Charles.
1
char huge_string_buf[MASSIVE_SIZE];
FILE stdout_ori=fdopen(stdout,"a");
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */
//Write to stdout_fdopen

Comments

0

I really don't like tricky games with file descriptors. Can't you modify the function so that it returns its data some other way than by writing to stdout?

If you don't have access to the source code, and you can't do that, then I would suggest breaking out the code that writes to stdout into a small separate program, and run that as another process. It is easy and clean to redirect output from a process (maybe through a named pipe), and then you will have no problem with outputting to stdout from the process that receives the data.

Also, depending on the sort of editing you wish to do, you might be better off using a high-level language like Python to edit the data.

3 Comments

well, i have it already done in python, but i need to reintegrate it back into ANSI C. So there is no way except calling another process? I am not sure, whether I do not violate the stack height somewhere.
What is constraining you to use C for this? A cleaner approach would be to pipe the output of your program through a filter than performs the needed changes (assuming command line usage, OS supports pipes, etc).
the constrains are the existing build and release-system + runtime on an microcontroller
0

If you are on a unix system, you can use pipes (you don't need to use fork). I'll try to thoroughly comment the code below so it doesn't look like I am doing any "magic."

#include <stdio.h>
#include <unistd.h>

int main()
{
    // Flush stdout first if you've previously printed something
    fflush(stdout);

    // Save stdout so it can be restored later
    int temp_stdout;
    temp_stdout = dup(fileno(stdout));

    // Redirect stdout to a new pipe
    int pipes[2];
    pipe(pipes);
    dup2(pipes[1], fileno(stdout));

    // Do whatever here. stdout will be captured.
    func_that_prints_something();

    // Terminate captured output with a zero
    write(pipes[1], "", 1);

    // Restore stdout
    fflush(stdout);
    dup2(temp_stdout, fileno(stdout));

    // Print the captured output
    while (1)
    {
        char c;
        read(pipes[0], &c, 1);

        if (c == 0)
            break;

        putc(c, stdout);
    }

    // Alternatively, because the output is zero terminated, you could
    // print using printf. You just need to make sure you read enough
    // from the captured output
    const int buffer_size = 1024;
    char buffer[buffer_size];
    read(pipes[0], buffer, buffer_size);
    printf(buffer);

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.