Мустафа Ид
Мустафа Ид

Reputation: 35

How can I read the error message from stderr?

We are studying Pipes and I have to create a C code that reads the error message from compiling another c program. I am using dup2 to copy stederr to writing side of the pipe, after that I use read method to read from the other side of the pipe but in some cases I am not able to read the whole error message. Here is what I am trying to do:

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

int main(int argc, char *argv[]) {
    const char *const cmd0 = argv[1];
    int fds_pair[2];
    pipe(fds_pair);
    
    pid_t pid;
    if ((pid = fork()) == 0) {

        close(fds_pair[0]);

        /// stderr <- pipe's write
        dup2(fds_pair[1], 2);
        execlp("g++", "g++", cmd0, "-o", "my_program", NULL);
        return 0;

    } else {
        close(fds_pair[1]);
    
        char expression[256];
        memset(expression, '\0', sizeof(expression));
        size_t readret;            
        readret = read(fds_pair[0], &expression, sizeof(expression));
        printf("%s", expression);

        while (readret > 0) {
            readret = read(fds_pair[0], expression, sizeof(expression));
            printf("%s", expression);
        }
    }
    close(fds_pair[0]);
    return 0;
}

the problem that I am facing is that when I try to get the error of program :

1   #include <stdio.h>
2
3   int main () {
4     ;float v_05778c89;
5     float v_ char v_ int v_ double v_ double v_
6   }

Here is the error message that I read from the code above:

main2.c: In function ‘int main()’:
main2.c:5:5: error: expected initializer before ‘float’
5 |     float v_
  |     ^~~~~

I don't get the error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘}’ token 6 | }

I am confused about what I am doing wrong or what I have to fix

Upvotes: 1

Views: 1439

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 755016

The error message you expect (expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘}’ token 6 | }) is typical of MS Visual C++, not GNU g++. That may be the main problem — unrealistic expectations.

I amended your code to include the first paragraph of code from my version, shown below, so that argc is used and it passes my default compilation options. When I run your amended code (source file re23.c compiled to re23), I get the output:

$ re23 main2.c
main2.c: In function ‘int main()’:
main2.c:5:12: error: expected initializer before ‘char’
    5 |   float v_ char v_ int v_ double v_ double v_
      |            ^~~~
main2.c: In function ‘int main()’:
main2.c:5:12: error: expected initializer before ‘char’
    5 |   float v_ char v_ int v_ double v_ double v_
      |            ^~~~
$

The message appears twice because you don't deal with the I/O from the child process (the C++ compiler) correctly. Basically, you need to read the data and check that it worked at all before you print anything.

Compilation (GCC 11.2.0 running on macOS Big Sur 11.6.4):

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common re23.c -o re23
$

I mentioned various problems in the comments, too. I believe they're all fixed in the code below, which works OK according to my standards. It's similar to yours, and is closely based on yours, but has a few key differences.

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

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s sourcefile.cpp\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    const char *const cmd0 = argv[1];
    int fds_pair[2];
    if (pipe(fds_pair) < 0)
    {
        fprintf(stderr, "%s: failed to create pipe: %d %s\n",
                argv[0], errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid < 0)
    {
        fprintf(stderr, "%s: failed to fork: %d %s\n",
                argv[0], errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)
    {
        dup2(fds_pair[1], STDERR_FILENO);
        close(fds_pair[0]);
        close(fds_pair[1]);
        execlp("g++", "g++", cmd0, "-o", "my_program", NULL);
        fprintf(stderr, "%s: failed to exec '%s': %d %s\n",
                argv[0], "g++", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    else
    {
        close(fds_pair[1]);
        char expression[256];
        ssize_t readret;
        while ((readret = read(fds_pair[0], &expression, sizeof(expression))) > 0)
        {
            printf("%.*s", (int)readret, expression);
        }
        close(fds_pair[0]);
        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            printf("%s: child %d exited with status 0x%.4X\n", argv[0], corpse, status);
    }
    return 0;
}

Here are some sample runs — I used source code re37.c for the source above, creating the program re37:

$ ./re37
Usage: ./re37 sourcefile.cpp
$ ./re37 nonexistent.cpp
cc1plus: fatal error: nonexistent.cpp: No such file or directory
compilation terminated.
./re37: child 4644 exited with status 0x0100
$ cat main2.c
#include <stdio.h>

int main () {
  ;float v_05778c89;
  float v_ char v_ int v_ double v_ double v_
}
$ ./re37 main2.c
main2.c: In function ‘int main()’:
main2.c:5:12: error: expected initializer before ‘char’
    5 |   float v_ char v_ int v_ double v_ double v_
      |            ^~~~
./re37: child 4670 exited with status 0x0100
$

Upvotes: 1

Related Questions