Thread: Writing a C Shell

  1. #1
    Registered User
    Join Date
    Oct 2011
    Posts
    4

    Writing a C Shell

    Hello everyone! I am playing around with learning to write a shell in C, but I'm running into some problems. First of all, I'm also very new to C in general (I'm a Java guy), so any mistakes you see are probably from my lack of knowledge.

    I have my code working with redirects (< and >) and appending (>>), but the background stuff is giving me trouble. I have it catching that there is an ampersand at the end of a line, but I don't know how to pass it to execvp, if that's even what I'm supposed to be doing. I'd like it to eventually distinguish between processes that can wait (vi) and those that can execute right away (ls).

    Here's what I have:
    Code:
    /*
     * This code implements a simple shell program
     * It supports the internal shell command "exit", 
     * backgrounding processes with "&", input redirection
     * with "<" and output redirection with ">".
     * However, this is not complete.
     */
    
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <signal.h>
    
    
    extern char **getline();
    
    
    /*
     * Handle exit signals from child processes
     */
    void sig_handler(int signal) {
      int status;
      int result = wait(&status);
      //UNCOMMENT
      //printf("Wait returned %d\n", result);
    }
    
    
    /*
     * The main shell function
     */ 
    main() {
      int i;
      char **args; 
      int result;
      int block;
      int output;
      int a_output;
      int input;
      char *output_filename;
      char *input_filename;
      char *append_filename;
    
    
      // Set up the signal handler
      sigset(SIGCHLD, sig_handler);
    
    
      // Loop forever
      while(1) {
    
    
        // Print out the prompt and get the input
        printf("->");
        args = getline();
        
        for(i = 0; args[i] != NULL; i++) {
          printf("Argument %d: %s\n", i, args[i]);
        }
        printf("\n");
        
        // No input, continue
        if(args[0] == NULL)
          continue;
    
    
        // Check for internal shell commands, such as exit
        if(internal_command(args))
          continue;
    
    
        // Check for an ampersand
        block = (ampersand(args) == 0);
    
    
        // Check for redirected input
        input = redirect_input(args, &input_filename);
    
    
        switch(input) {
        case -1:
          printf("Syntax error!\n");
          continue;
          break;
        case 0:
          break;
        case 1:
          printf("Redirecting input from: %s\n", input_filename);
          break;
        }
    
    
        // Check for redirected output
        output = redirect_output(args, &output_filename);
    
    
        switch(output) {
        case -1:
          printf("Syntax error!\n");
          continue;
          break;
        case 0:
          break;
        case 1:
          printf("Redirecting output to: %s\n", output_filename);
          break;
        }
        
        // Check for appended output
        a_output = append_output(args, &append_filename);
        // a_output = 0;
    
    
        switch(a_output) {
        case -1:
          printf("Syntax error!\n");
          continue;
          break;
        case 0:
          break;
        case 1:
          printf("Appending output to: %s\n", append_filename);
          break;
        }
        
        // Do the command
        do_command(args, block, 
               input, input_filename, 
               output, output_filename,
               a_output, append_filename);
      }
    }
    
    
    /*
     * Check for ampersand as the last argument
     */
    int ampersand(char **args) {
      int i;
    
    
      for(i = 1; args[i] != NULL; i++) ;
    
    
      if(args[i-1][0] == '&') {
        free(args[i-1]);
        args[i-1] = NULL;
        return 1;
      } else {
        return 0;
      }
      
      return 0;
    }
    
    
    /* 
     * Check for internal commands
     * Returns true if there is more to do, false otherwise 
     */
    int internal_command(char **args) {
      if(strcmp(args[0], "exit") == 0) {
        exit(0);
      }
    
    
      return 0;
    }
    
    
    /* 
     * Do the command
     */
    int do_command(char **args, int block,
               int input, char *input_filename,
               int output, char *output_filename,
               int a_output, char *append_filename) {
      
      int result;
      pid_t child_id;
      int status;
    
    
      // Fork the child process
      child_id = fork();
    
    
      // Check for errors in fork()
      switch(child_id) {
      case EAGAIN:
        perror("Error EAGAIN: ");
        return;
      case ENOMEM:
        perror("Error ENOMEM: ");
        return;
      }
    
    
      if(child_id == 0) {
    
    
        // Set up redirection in the child process
        if(input)
          freopen(input_filename, "r", stdin);
    
    
        if(output)
          freopen(output_filename, "w+", stdout);
          
        if(a_output)
          freopen(append_filename, "a+", stdout);  
        
        // Execute the command
        result = execvp(args[0], args);
    
    
        exit(-1);
      }
    
    
      // Wait for the child process to complete, if necessary
      if(block) {
        //UNCOMMENT
        //printf("Waiting for child, pid = %d\n", child_id);
        result = waitpid(child_id, &status, 0);
      }
    }
    
    
    /*
     * Check for input redirection
     */
    int redirect_input(char **args, char **input_filename) {
      int i;
      int j;
    
    
      for(i = 0; args[i] != NULL; i++) {
    
    
        // Look for the <
        if(args[i][0] == '<') {
          free(args[i]);
    
    
          // Read the filename
          if(args[i+1] != NULL) {
        *input_filename = args[i+1];
          } else {
        return -1;
          }
    
    
          // Adjust the rest of the arguments in the array
          for(j = i; args[j-1] != NULL; j++) {
        args[j] = args[j+2];
          }
    
    
          return 1;
        }
      }
    
    
      return 0;
    }
    
    
    /*
     * Check for output redirection
     */
    int redirect_output(char **args, char **output_filename) {
      int i;
      int j;
    
    
      for(i = 0; args[i] != NULL; i++) {
            // Look for the >
            if(args[i][0] == '>') {
                if(args[i+1][0] == '>'){
                    return 0;
                }else{
                  free(args[i]);
    
    
                  // Get the filename 
                  if(args[i+1] != NULL) {
                    *output_filename = args[i+1];
                  } else {
                    return -1;
                  }
    
    
                  // Adjust the rest of the arguments in the array
                  for(j = i; args[j-1] != NULL; j++) {
                    args[j] = args[j+2];
                  }
    
    
                  return 1;
                }
            }
      }
    
    
      return 0;
    }
    
    
    /*
     * Check for append redirection
     */
    int append_output(char **args, char **output_filename) {
      int i;
      int j;
    
    
      for(i = 0; args[i] != NULL; i++) {
    
    
        // Look for the >>
        if((args[i][0] == '>') && (args[i+1][0] == '>')) {
          free(args[i]);
    
    
          // Get the filename 
          if(args[i+2] != NULL) {
            *output_filename = args[i+2];
          } else {
            return -1;
          }
    
    
          // Adjust the rest of the arguments in the array
          for(j = i; args[j-1] != NULL; j++) {
            args[j] = args[j+3];
          }
    
    
          return 1;
        }
      }
    
    
      return 0;
    }
    Any advice would be helpful.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > Any advice would be helpful.
    Mmm - kay....

    > I have my code working
    Really?
    Something really similar from 2 years ago -> Creating A Simple Unix Shell Using C - C And C++ | Dream.In.Code

    Advice 1
    We can use google just as well as you.
    Advice 2
    Try your homework for yourself. Because dumping found code and a list of enhancements isn't fooling anybody.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Oct 2011
    Posts
    4
    I'm a little confused about what you're getting at. My professor assigned us an assignment to make a C Shell. He gave us some base code, and we were to implement a variety of things, and I am stuck on the step where I need to execute a command in the background. I came here for help.

    I didn't "find" this code. It's what my professor gave me. I assume that whoever posted in that link you showed must have had the same professor as I do now.

    All I want is some direction as to what I'm doing wrong, or where I can start with this background stuff. I'm sorry if what I did wasn't correct, but I'm stressed beyond belief with this project. I've never coded anything in C before in my life. The curriculum at my university changed from C/C++ to Java the year I got here, and this OS class I'm in is the first experience I've had with anything that isn't Java.

    I'm sorry if I offended you with what looked like a copy/paste job, but that wasn't the case. This is my code, altered from what the professor gave us.

  4. #4
    Registered User
    Join Date
    Sep 2011
    Posts
    10
    You still want us to do something that you have been assigned. If you don't understand what you are doing wrong, perhaps you should ask yourself if programming is for you.

  5. #5
    Registered User
    Join Date
    Oct 2011
    Posts
    4
    Oh! No! I'm sorry if that's what is sounds like. I came for help with this, not the answer. I'm just so lost. I'm a 4th year Computer Science major at my university and this is the first time I've ever used LINUX or C. My university has admitted that it was a flaw for them to keep them from us until now, but that hasn't kept them from assigning this (any many more to come) assignment.

  6. #6
    Registered User
    Join Date
    Sep 2011
    Posts
    10
    What is the name of your university?

  7. #7
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by Ganeron View Post
    but I don't know how to pass it to execvp
    When in doubt, RTFMP!


    Quzah.
    Hope is the first step on the road to disappointment.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. writing good code is like writing an artistic expression
    By renzokuken01 in forum C Programming
    Replies: 5
    Last Post: 02-03-2011, 08:48 PM
  2. Qustion about writing a shell
    By bchaffin72 in forum Linux Programming
    Replies: 17
    Last Post: 05-30-2009, 05:23 AM
  3. C Shell
    By CrazyShellsSlam in forum C Programming
    Replies: 0
    Last Post: 12-11-2007, 12:22 PM
  4. Writing A Dos Shell AkA Win 3.11 Mark 2 :p
    By Arius Myst in forum A Brief History of Cprogramming.com
    Replies: 5
    Last Post: 06-22-2002, 04:45 PM