KerDam
KerDam

Reputation: 421

C putchar doesn't work after stdout is redirected

I am trying to implement shell redirection, using this I redirect stdout to fd1

int redirectOut(int fd1)
{
    fflush(stdout);
    int fd2 = dup(STDOUT_FILENO);
    dup2(fd1, STDOUT_FILENO);
    close(fd1);
    return fd2;
}

I then fork and call an executable, it works except in the case where the executable uses putchar.

On the putchar man page it is written that it uses stdout.

putchar(c); is equivalent to putc(c,stdout).

Why doesn't putchar write anywhere neither in the standard output nor the file I redirected the stream to ?

I tried changing putchar to putc but it didn't help, it might have something to do with the fact that stdout if a *FILE and STDOUT_FILENO an int

How can I make my code work and why does it work with printf which uses (code for printf)

done = vfprintf (stdout, format, arg);

EDIT MORE CODE

int executeBlocs(execBloc *bloc,int fileIn,int fileOut){
    if(bloc->first != NULL){
      if (strcmp(bloc->ope, ">") == 0){
         int out = open(bloc->command[0], O_WRONLY | O_CREAT | O_TRUNC , 0644);
         int returnCode = executeBlocs(bloc->first, STDIN_FILENO, out);
         redirectOut(fileOut);
         redirectIn(fileIn);
         return returnCode;
      }
    }
    else{
        redirectIn(fileIn);
        redirectOut(fileOut);
        return call(bloc->nbWords, bloc->command);
    }
}

execBloc is a struct that contains a command to execute (or a file name) an operator (>> , | , > ...) and a reference to the another bloc that contains the rest of the command.

If the user enter cat /tmp/testCat > /tmp/testCatRedirection

then a first structure will be created containing the operator > and the command /tmp/testCatRedirection and first which is a reference to the second structure containing the command cat /tmp/testCat

int call(int argc, char const *argv[]) {
  if (argc > 0){
      if (executeProgram(argv) == 1) return 1;

      if (executeStandardLibrary(argc, argv) == 1) return 1;

      if (executeDynamicLibrary(argc, argv) == 1) return 1;
  }
  return -1;
}


int executeProgram(char const *argv[]){
    //Creation de la chaine de caractère /home/kerdam/cbin/nonExecutable
    char *path = strdup(binFolder);
    strcat(path, argv[0]);


    //Test si le fichier existe et est executable
    if (access(path, F_OK|X_OK) != -1){
    //Le fichier existe et on peut l'éxecuter
    int pid = fork();

    // Error
    if (pid == -1){
      return -1;
    }
    //Fils
    else if (pid == 0) {
        // Executer la commande
        execv(path, argv);
        return 1;
    }

    // Parent process
    else {
        // Wait for child process to finish
        int childStatus;
        waitpid(pid, &childStatus, 0);
        return 1;
    }
  }
  else return -1;
}

Finally the code of the program I am trying to execute

#include<stdio.h>
#include<string.h>
#define MAX_FILE_NAME_CHARS 255
int main(int argc, char *argv[])
{
    FILE *fp;
    char file_name[MAX_FILE_NAME_CHARS], ch;
    int i;

     /*
      * after creating a.out, rename it as mycat for our own cat command
      * and it usage is same as standard cat command
      */
    if(argc<=1){
        printf("Utiliser cat avec aumoin un argument (un fichier) <nomfichier> \n");
        return 0;
    }

     /*
      * This is for multiple file in argument
      */
    for(i=1; i<=argc;i++){
        strncpy(file_name, argv[i], MAX_FILE_NAME_CHARS);

        fp=fopen(file_name, "r");
        if(fp == NULL) {
           printf("%s: No such file or directory\n", file_name);
           return 0;
        }

        /*
         * read file and feed contents to STDIO
         */
        while((ch=fgetc(fp)) != EOF || ch == '}'){
            putchar(ch);
        }
        fclose(fp);
    }
    return 0;
}

Remark

I should not change the code of the executable I am trying to execute as the users of my shell should be able to execute their programs without restriction on what function they can use.

Upvotes: 0

Views: 614

Answers (1)

Anton Todua
Anton Todua

Reputation: 687

I've been face with the same problem and found the solution.

Look at the code which reads a file argv[1] and writes its content to file argv[2] without spaces and line feeds.

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

int main(int argc, const char* argv[]) {
  if (argc != 3) {
    printf ("Expected exactly two arguments\n");
    return 1;
  }

  int rd = open(argv[1], O_RDONLY);
  if (rd == -1) {
    printf ("Failed to open file %s\n", argv[1]);
    return 1;
  }

  int wd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);
  if (wd == -1) {
    printf ("Failed to open file %s\n", argv[2]);
    return 1;
  }

  int temp_in = dup(0);
  int temp_out = dup(1);

  dup2(rd, 0);
  dup2(wd, 1);

  close(rd);
  close(wd);

  int written_bytes = 0;

  int c;
  while ((c = getchar()) != EOF) {
    if (c == ' ' || c == '\n') {
      continue;
    }

    putchar(c);
    written_bytes++;
  }

  // The instruction below is critically important, because
  // putchar by default writes char to an internal buffer,
  // so we have to send it to the file descriptor manually
  fflush(stdout);

  dup2(temp_in, 0);
  dup2(temp_out, 1);

  printf ("%d bytes have been written\n", written_bytes);
  return 0;
}

Upvotes: 0

Related Questions