Search code examples
cfileiopipesystem-calls

Strange letter when passing character through a pipe


I've an input file whose content is abcefghz. I want to use a pipe so that a child process p1 sends 1-letter per time to his parent process which will converts this string using the ASCII code+1 (abcefghz will become cdfghi{). This new string will be send through another pipe to another child process which will print the result on an output file.

This is the code:

int main (int argc, char **argv)
{  
    pid_t pid1, pid2;
    int inputFile, outputFile;
    char stringaDalFile[256];
    char successivo;
    char stringaRisultato[256];
    int fd1[2], fd2[2]; // Pipe



    inputFile = open(argv[1], O_RDONLY);
    outputFile = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);

    pipe(fd1);
    pipe(fd2);

    pid1 = fork();
    if (pid1 == 0) { 

        while ( (nread=read(inputFile, stringaDalFile, 1)) > 0) {
            close(fd1[0]);
            write(fd1[1], stringaDalFile, 1);
        }

        close(inputFile);
    }
    else { 
        close(fd1[1]);
        while ( read(fd1[0], stringaDalFile, 1) > 0 ) {

            successivo = converti(stringaDalFile[0]);

            write(fd2[1], &successivo, 1);
        }
    }

    pid2 = fork();
    if (pid2 == 0) { 

        close(fd2[1]);

        if (read(fd2[0], stringaRisultato, 1) == -1) {
            perror("Errore");
            exit(1);
        } 
        else {
            while ( read(fd2[0], stringaRisultato, 1) > 0 ) {
                write(STDOUT_FILENO, stringaRisultato, strlen(stringaRisultato)); //dbg
write(outputFile, stringaRisultato, strlen(stringaRisultato));
            }
        }

        close(outputFile);

        exit(0);
    }

    return 0;
}

char converti (char carattere) 
{
    return carattere+1;
}

Unfortunately, this seems to not work at 100%, the string is converted but the program enters what seems an infinite loop:

string is converted but infinite loop

If I CTRL-C and gedit file2.txt, this is its content:

stranger character.

How do I fix this?


Solution

  • There are a few problems in your code:

    1. The real problem, here:

      write(outputFile, stringaRisultato, strlen(stringaRisultato));
      

      you're using strlen(stringaRisultato) when stringaRisultato only has one valid character at the beginning and no NUL-terminator after it. Just use 1 instead.

    2. You are only reading one character at a time, you don't need a string of 256 characters. Change stringaDalFile and stringaRisultato into two single char variables: carattereDalFile and carattereRisultante.

    3. In the first child (inside if (pid1 == 0)) you are doing close(fd1[0]) in a loop. You should move it out of the while.

    4. Again in the first child, you're not doing exit(0). This is what causes your program to keep running.

    5. This line inside the second child:

      if (read(fd2[0], stringaRisultato, 1) == -1) {
      

      is only useful if you want to skip the first character. Is that intended? If not, remove that if and only use the while (read(...) > 0).

    6. You are writing everything to the pipe fd2[1] before starting the second child (that will read from it). If the input file is too large (some KB) this will result in filling the pipe internal buffer and will block your program on the next write() that it tries to perform, making it never end. To fix this, you should start the two child processes together. This would require changing the structure and logic of the code, but it's doable.

      Since I don't think that's the real problem here and this program is most probably written for educational purposes I'll leave that to you and fix the rest of the above points.


    Working code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/types.h>
    
    char converti (char carattere);
    
    int main (int argc, char **argv)
    {
        pid_t pid1, pid2;
        int inputFile, outputFile;
        char carattereDalFile, carattereRisultante, successivo; // renamed
        int fd1[2], fd2[2];
    
        inputFile = open(argv[1], O_RDONLY);
        outputFile = open(argv[2], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    
        pipe(fd1);
        pipe(fd2);
    
        pid1 = fork();
        if (pid1 == 0) {
            close(fd1[0]); // <==  moved out of the while loop
    
            while (read(inputFile, &carattereDalFile, 1) > 0) {
                write(fd1[1], &carattereDalFile, 1);
            }
    
            close(inputFile);
            exit(0); // <== added
        } else {
            close(fd1[1]);
    
            while (read(fd1[0], &carattereDalFile, 1) > 0) {
                successivo = converti(carattereDalFile);
                write(fd2[1], &successivo, 1);
            }
        }
    
        pid2 = fork();
        if (pid2 == 0) {
            close(fd2[1]);
    
            if (read(fd2[0], &carattereRisultante, 1) == -1) {
                perror("Errore");
                exit(1);
            } else {
                while (read(fd2[0], &carattereRisultante, 1) > 0) {
                    write(outputFile, &carattereRisultante, 1);
                }
            }
    
            close(outputFile);
            exit(0);
        }
    
        return 0;
    }
    
    char converti (char carattere)
    {
        return carattere+1;
    }