how to store output from "bc" into a variable?

431 views Asked by At

What this program is supposed to do is ask user a simple arithmetic question, e.g. 5 + 7 and then check the answer with "bc" (whether it's correct).

I have the following code, but I don't understand how to edit it to to store the result from "5 + 7" into a variable (currently the result goes into STDOUT).

Any help is welcome.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int main( int argc, char *argv[], char *env[] )
{

    char *expr = "5 + 7\n";
    int answer;

    printf("%s = ", expr);
    scanf("%d", &answer);


    int pfds[2];
    pipe(pfds);

    if (!fork()) {
        close(1);       /* close normal stdout */
        dup(pfds[1]);   /* make stdout same as pfds[1] */
        close(pfds[0]); /* we don't need this */
        printf("%s\n", expr);

        /***********************/
        /* How to store printf()'s output into a variable? */

        exit(0);
    } else {
        close(0);       /* close normal stdin */
        dup(pfds[0]);   /* make stdin same as pfds[0] */
        close(pfds[1]); /* we don't need this */
        execlp("bc", "bc", NULL);
    }


   return 0;
}
3

There are 3 answers

1
Medinoc On BEST ANSWER

You need to create a second pipe and redirect stdout to it in the child process.

0
Gabson On

You can simply read STOUD or read on your pipe's Output. Then a call to Read() and to Atoi might do the work. Atoi man page here

I can't code it for you, but here the logic

    `int fds[2];
     pipe(fds);
     dup2(fds[1], stdout);
     read(fds[1], buf, buf_sz);
     int ResultOfbc = Atoi(buf)`
0
brunch875 On

Ooof oof, this stuff I remember doing last year. I was basically a program which used my own telnet clone to communicate with the internet. Problem is telnet used stdin and stdout to work.

It looks like you have the same situation! What I did to solve this was to fork a process, grab stdin and stdout and put them in pipes and then overwrite the forked process image with an invocation of telnet (which now instead of stdin and stdout, uses those pipes).

You need ONE pipe to send text to bc and ANOTHER to receive from bc. If you use a single pipe for everything, chances are you'll end up reading what you sent to bc and mix data.

WARNING: MASSIVE AMOUNT OF CODE INCOMING. I'm sure you won't need to understand everything, since I use threads to write and read simultaneously and select() to see if there's anything to read in the pipe. VERY IMPORTANT!!! When communications break, your process will receive SIGPIPE which terminates not cleanly (if you're using dynamic memory or stuff like that). And you MUST fflush(outpipe) or else bc won't receive it. (This is because system only flushes when it finds '\n' or something like that, if I recall correctly). I put all the code just in case you want to read what X does. But what you need is only the small fork right after the comment "LOCAL FUNCTIONS END HERE"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include "irc.h"
#include "coloring.h"
#include "rtsp.h"

#define BUFFERSIZE 255
int main (int argc, char *argv[]) {

/* XXX: When kid dies, program doesn't exit */

char *serverName, *port, *nick, *channel;
int ptelnetin[2];
int ptelnetout[2];
FILE *fpipes[2];
bool running = true;
pid_t kid;
pthread_t pthread_input, pthread_output;


/************************************************

  LOCAL FUNCTIONS START HERE

  ***********************************************/



void *inputprocess(void *pipes) {

    bool bracket;
    int i;
    fd_set rfds;
    struct timeval tv;
    tv.tv_sec = 0.2;
    tv.tv_usec = 0;
    char buffer[BUFFERSIZE];
    FILE *out = ((FILE **) pipes)[1];


    while (running){ 
        FD_ZERO(&rfds);
        FD_SET(fileno(stdin), &rfds);
        switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
            case -1:
                fprintf(stderr, "Error reading data at select() Line %d, File %s\n", __LINE__, __FILE__);
                running = false;
                break;

            case 0: 
                /* There's no data avaiable just yet.
                   Do nothing and keep checking */
                break;

            default:
                /* This check needs to be done;
                   select isn't completely reliable */
                if(!fgets(buffer, BUFFERSIZE, stdin)) {
                    running = false;
                    break;
                }
                /* Check message not to contain brackets*/
                for (i = 0, bracket = false; running && !bracket && buffer[i] && i < BUFFERSIZE; i++) {
                    if (buffer[i] == '[' || buffer[i] == ']') {
                        PRINT_YELLOW;
                        printf("Use of brackets not allowed\n");
                        RESET_COLOR;
                        fflush(stdout);
                        bracket = true;
                        break;
                    }
                }
                if (running && !bracket) ircInputWrite(out, buffer);
                fflush(out);
        }

    }
}

void *outputprocess(void *pipes){


    fd_set rfds;
    struct timeval tv;
    tv.tv_sec = 0.2;
    tv.tv_usec = 0;
    char buffer[BUFFERSIZE];
    char from[100];
    FILE *in = ((FILE **) pipes)[0];
    FILE *out = ((FILE **) pipes)[1];


    while (running){ 
        FD_ZERO(&rfds);
        FD_SET(fileno(in), &rfds);
        switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
            case -1:
                fprintf(stderr, "Error reading data at select() Line %d, File %s\n", __LINE__, __FILE__);
                running = false;
                break;

            case 0: 
                /* There's no data avaiable just yet. */
                /* Select sometimes returns 0 when there IS
                   data to read so we'll read anyway */

            default:
                /* This check needs to be done;
                   select isn't completely reliable */
                if(!fgets(buffer, BUFFERSIZE, in)) {
                    running = false;
                    break;
                }

                switch(ircWhatsthis(buffer)) {
                    case iPING:
                        PRINT_BLUE;
                        printf("PingPong!\n");
                        RESET_COLOR;
                        ircPingPong(out, buffer); fflush(out);
                        fflush(stdout);
                        break;
                    case iROOMMSG:
                        if (ircUnpackPRIVMSG(from, buffer, buffer)) {
                            PRINT_BRIGHT_RED;
                            fprintf(stdout, "Malformed private message received\n");
                            RESET_COLOR;
                        }
                        else {
                            PRINT_CYAN;
                            printf("<%s>: ", from);
                            puts(buffer);
                            RESET_COLOR;
                        }

                        fflush(stdout);
                        break;

                    case iPRIVMSG:
                        fflush(stdout);
                        if (ircUnpackPRIVMSG(from, buffer, buffer)) {
                            PRINT_BRIGHT_RED;
                            fprintf(stdout, "Malformed private message received\n");
                            RESET_COLOR;
                            fflush(stdout);
                        }
                        else {
                            if (rtspExecBrackets(out, from, buffer)) {
                                PRINT_BRIGHT_MAGENTA;
                                printf("[%s]: ", from);
                                puts(buffer);
                                RESET_COLOR;
                                fflush(stdout);
                            }
                        }

                        break;

                    case iERROR:
                        PRINT_BRIGHT_RED;
                        fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
                        RESET_COLOR;
                        break;
                    case iOK:
                        PRINT_BRIGHT_CYAN;
                        fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
                        RESET_COLOR;
                        break;
                    default:
                        PRINT_BRIGHT_BLACK;
                        fwrite(buffer, sizeof(char), strlen(buffer)+1, stdout);
                        RESET_COLOR;
                }

                fflush(stdout);
        }

    }
}

void terminate(int signum) {
    /* XXX irc.c calls rtsp.c which uses threads.
       These threads never clean up if exiting via ^C
    */
    RESET_COLOR;
    running = false;
    /*Close IO*/
    fclose(fpipes[0]);
    fclose(fpipes[1]);
    /* Call child */
    kill(kid, SIGINT);
    wait(NULL);
    exit(0);

}


/************************************************

  LOCAL FUNCTIONS END HERE

 ***********************************************/

signal(SIGPIPE, terminate);
signal(SIGINT, terminate);



/* Get parameters */
if (argc != 5) {
    fprintf(stderr, "Usage:\n %s <server> <port> <nick> <channel>\n", argv[0]);
    return -1;
}

serverName = argv[1];
port = argv[2];
nick = argv[3];
channel = argv[4];

/* Startup pipes */
pipe(ptelnetin);
pipe(ptelnetout);

/* Launch telnete */
switch (kid = fork()) {
    case -1:
        perror("OMG ABORTION at main");
        exit(-2);
    case 0: /* CHILD */
        /*Overwrite stdin with pipein and discard pipe*/
        dup2(ptelnetin[0], 0);
        close(ptelnetin[0]);
        close(ptelnetin[1]);
        /*Overwrite stdout with pipeout and discard pipe*/
        dup2(ptelnetout[1], 1);
        close(ptelnetout[0]);
        close(ptelnetout[1]);
        /*Overwrite process image with telnete*/
        execlp("./telnete", "./telnete", argv[1], argv[2], (char *) NULL);
        perror("Call to exec failed at main");
        exit(-3);
    default: /* PARENT */
    /* Close reading end of pipein */
        close(ptelnetin[0]);
    /* Close writing end on pipeout */
        close(ptelnetout[1]);
}


/* Turn (fileno) into (FILE *) */
fpipes[1] = fdopen(ptelnetin[1],"w");

if(!fpipes[1]) {
    perror("Error at fdopen(in) at main");
    kill(kid, SIGINT);
    abort();
}


fpipes[0] = fdopen(ptelnetout[0],"r");
if(!fpipes[0]) {
    perror("Error at fdopen(out) at main");
    kill(kid, SIGINT);
    abort();
}




/* Sleep for a few seconds so server doesn't ignore it */
PRINT_YELLOW;
printf("Logging in IRC...\n");
RESET_COLOR;
fflush(stdout);
if (ircRegister(argv[3], fpipes[1], fpipes[0])) {
    fprintf(stderr, "Error registering in IRC.\n");
    terminate(-1);
}

PRINT_YELLOW;
printf("Joining room %s\n", argv[4]);
RESET_COLOR;
ircJOIN(fpipes[1], argv[4]);
fflush(fpipes[1]);


/* Launch threads */
if (pthread_create(&pthread_input, NULL, inputprocess, fpipes)){
    fprintf(stderr,"Couldn't launch input thread");
    kill(kid, SIGINT);
    abort();
}
if (pthread_create(&pthread_output, NULL, outputprocess, fpipes)){
    fprintf(stderr,"Couldn't launch output thread");
    kill(kid, SIGINT);
    abort();
}

/* Wait for threads */
if (pthread_join(pthread_input,NULL)){
    fprintf(stderr, "Error joining thread.\n");
}
if (pthread_join(pthread_output,NULL)){
    fprintf(stderr,"Error joining thread.\n");
}


terminate(0);





}

I'll put the key fragment here so it's clearer:

/* Startup pipes */
pipe(ptelnetin);
pipe(ptelnetout);

/* Launch telnete */
switch (kid = fork()) {
    case -1:
        perror("OMG ABORTION at main");
        exit(-2);
    case 0: /* CHILD */
        /*Overwrite stdin with pipein and discard pipe*/
        dup2(ptelnetin[0], 0);
        close(ptelnetin[0]);
        close(ptelnetin[1]);
        /*Overwrite stdout with pipeout and discard pipe*/
        dup2(ptelnetout[1], 1);
        close(ptelnetout[0]);
        close(ptelnetout[1]);
        /*Overwrite process image with telnete*/
        execlp("./telnete", "./telnete", argv[1], argv[2], (char *) NULL);
        perror("Call to exec failed at main");
        exit(-3);
    default: /* PARENT */
    /* Close reading end of pipein */
        close(ptelnetin[0]);
    /* Close writing end on pipeout */
        close(ptelnetout[1]);
}


/* Turn (fileno) into (FILE *) */
fpipes[1] = fdopen(ptelnetin[1],"w");

if(!fpipes[1]) {
    perror("Error at fdopen(in) at main");
    kill(kid, SIGINT);
    abort();
}


fpipes[0] = fdopen(ptelnetout[0],"r");
if(!fpipes[0]) {
    perror("Error at fdopen(out) at main");
    kill(kid, SIGINT);
    abort();
}

After doing this you can read bc's result from the (FILE *) fpipes[0] and write to it in fpipes[1]. Remember to fflush(fpipes[1]) after each write. Treat those two as you would with any file.