alwaz on9 0 Newbie Poster

Hi all,
I was asked to write a shell program in c in unix....basically what i would like to include are the use of dup2 to implement redirection ( < or >) and the use of dup2() and the pipe() system call to implement the '|' symbol on the command line...and so below is the code...basically it works for those command like
ls -l | grep test , ls -l > file but it doesn't work when i enter ls -l | grep test > file2 as the output of ls -l | grep test doesn't written into file2... i added some of the printf at some of the place to check...
is there anyone can help me to figure out what's the problem...thanks in advance!
any help will be greatly appreciated:)

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

#define SIZE 80

/********************** Parse the Command Line by | and store in temp[i] ***********************************/
void ParseInput(char *buf, char **temp, int *count)
{
        int i = 0;
        int nos, s, pos;
        char *command;
        char space[] = " \t";   // remove space or tab before the command, if any
        char nl[] = "\t\n";     // remove new lines or tab, if any

        command = strtok(buf,"|");              // tokenize the command line by |
        while(command != NULL)
        {
                nos = strspn (command, space);  // return the number of spaces or tab before the command
                for (s = 0; s < nos; s++)
                        ++command;              // increment the pointer of command to remove the spaces or tabs, if any
                if (pos = strcspn (command, nl))// find if there's any new line or tab after the command
                        command[pos] = '\0';    // replace it with a null, if any                       
                for (;;)                                // make a loop
                {
                        int n = strlen (command) - 1;   // last character position in command
                        if (command[n] == ' ')          // if there's a space 
                                command[n] = '\0';      // makes the string shorter
                        else 
                                break;                  // out of the loop, if any non-white space is found
                }                       
                temp [i] = (char *)command;             // store each commands into the array
                command = strtok(NULL,"|");             // tokenize the command line by |
                i++;                            // Increment i
        }       
        *count = i;                             // Count equals to i
}

/******************** Parse the Command into arguments and exec the arguments **************************/
void execute(char *temp, int r, int w)
{
        pid_t pid;
        char *argument;
        char *argv[80]={0};
        char *input = "<";
        char *output = ">";
        int *array2[SIZE];              // create an array of int * 
        int k = 0;
        int fds[2];
        pid = fork();

        if (pid == 0)   /* This is the child process */
        {
                memset(argv,0,sizeof(argv));            // Remove the elements in argv array
                argument = strtok((char *)temp," \t\n");        // tokenize the command into arguments
                while ((char *)argument != NULL)
                {
                        if (strcmp((char *)argument,input) == 0)        // Check if the '<' exists in the argv array
                        {
                                argument = strtok(NULL," \t\n");        // tokenize the command into arguments
                                r = CreateInputRedirectFile(argument);  // Return a file descriptor from CreateInputRedirectFile
                        }
                        else if (strcmp((char *)argument,output) == 0)  // Check if the '>' exist in the argv array
                        {
                                argument = strtok(NULL," \t\n");        // tokenize the command into arguments
                                w = CreateOutputRedirectFile(argument); // Return a file descriptor from CreateOutputRedirectFile
                                printf("write to file\n");
                        }
                        else
                        {
                                printf("%s\n",argument);
                                argv[k] = argument;             // store each arguments into the array
                                k++;                                    // Increment k

                        }
                        argument = strtok(NULL," \t\n");        // tokenize the command into arguments
                }

                printf("%s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3], argv[4]);

                if (r != -1)            // if fds[0] is valid
                {
                        dup2(r,0);      // Reassign stdin to r = fds[0] end of pipe
                }

                if (w != -1)            // if fds[1] is valid
                {
                        dup2(w,1);      // Reassign stdout to w = fds[1] end of pipe
                }

                close(r);       // Not going to write in this child process, so we can close this end of pipe
                close(w);       // Not going to write in this child process, so we can close this end of pipe

                execvp(argv[0],argv);           // execute the given command
                write(1,"exec fail\n",10);      // print this message if fails to execvp
                exit(1);
        }
        else if (pid < 0)               // error
        {
                printf("fork failed");
                exit(1);
        }
}

/**************** Create a Pipe function if there exists a pipe in the command line ****************************/

int *CreatePipe()
{
        int *fds = (int *)calloc(2, sizeof(int));       // Dynamically allocated memory
        pipe(fds);                                      // Create a pipe

        return (int *)fds;                              // Return the two file descriptors
}

/**************** Create a Input Redirect function if there exists a < in the command line **********************/

int CreateInputRedirectFile(char *argument)
{
        int fdi;        // file descriptor
        fdi = open(argument, O_RDONLY); // Open the input files
        if (fdi < 0)
                printf("Open read fail");               // Error

        return fdi;                             // Return a file descriptor
}

/**************** Create a Output Redirect function if there exists a > in the command line *********************/

int CreateOutputRedirectFile(char *argument)
{
        int fdo;        // file descriptor
        mode_t mode = S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR;                    // permission mode for the created file
        fdo = open(argument, O_CREAT | O_RDWR | O_TRUNC, mode);         // Open the output files
        if (fdo < 0)    
                printf("Open file fail");               // Error

        printf("write successful\n");

        return fdo;                             // Return a file descriptor
}

/********* Main function that pass a particular parameters from ParseInput function to Execute function ****************/

int main()
{
        int j = 0;
        char buf[80];           // command line
        char *temp[80]={0};     // an array to store the commands
        int count;
        pid_t pid;
        int read_fds, write_fds;
        int *array[SIZE];               // create an array of int * 
        int invalid = -1; 

        printf("prompt-> ");
        while (strcmp(fgets((char *)buf, 80, stdin), "exit\n") !=0)  // user command line
        {
                if(strcmp(buf, "\n")!=0)  // user command line
                {
                        memset(temp,0,sizeof(temp));            // Remove the elements in temp array
                        memset(array,0,sizeof(array));          // Remove the elements in array 
                        int count = 0;                          // Initialize count to 0
                        int j = 0;                              // Initialize j to 0
                        ParseInput(buf, temp, &count);          // Parse the user input line
                        
                        if (count == 1)                         // If count is 1 (only one command)
                        {
                                execute(temp[j], invalid, invalid);     // call execute func
                        }
                        if (count > 1)                          // for more than one command (with pipes)
                        {
                                array[j] = CreatePipe();        // store the return value of CreatePipe to array[j]
                                write_fds = array[0][1];        // copy the write end of the pipe to write_fds
                                execute(temp[j], invalid, write_fds);   // call the execute func and pass the end of pipe
                                j++;                                    // increment of j
 
                                for(j = 1; j < count - 1; j++)                  // for each command
                                {
                                        array[j] = CreatePipe();                // create another pipe
                                        read_fds = array[j-1][0];               // copy the read end of previous pipe
                                        write_fds = array[j][1];                // copy the write end of current pipe
                                        execute(temp[j], read_fds, write_fds);  // call the execute func
                                }
                
                                if (j == count - 1)                             // for the last command
                                {
                                        read_fds = array[j-1][0];               // copy the read end of previous pipe
                                        execute(temp[j], read_fds, invalid);    // call the execute func
                                }
                        }

                }
                else 
                        printf("prompt-> ");

                memset(buf,0,sizeof(buf));              // Remove all the elements in buf
        }
        return 0;
}

prompt-> ls -l | grep test > file
ls
-l
ls -l (null) (null) (null)
grep
test
write successful
write to file
grep test (null) (null) (null)

prompt-> ls -l file
ls
-l
file
ls -l file (null) (null)
-rw-r----- 1 cpfoo ugrad 0 2011-04-23 09:53 file <-- nothing is written 0 bytes??

prompt-> ls -l > file <-- without | in btw commands
ls
-l
write successful
write to file
ls -l (null) (null) (null)

prompt-> ls -l file
ls
-l
file
ls -l file (null) (null)
-rw-r----- 1 cpfoo ugrad 519 2011-04-23 09:53 file <--ls -l is written into file with 519 bytes and i checked using cat file, everything in ls -l was written too.....