Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Overview Writing in the C language, implement a very basic shell, called smash .

ID: 3587853 • Letter: O

Question

Overview

Writing in the C language, implement a very basic shell, called smash. In this project, we will work on processing strings and using the appropriate system calls.

Build System setup

Before we can start writing code we need to get our build system all setup. This will involve writing a very simple makefile. You should leverage your Makefile project for this part of the assignment!

Write a Makefile that provides the three targets listed below

all - The default rule. Builds your project.

smash - Builds your smash shell. Initially only one file smash.c

Make sure to use macros!

clean - Cleans your build directory and deletes any generated files.

Implement basic string processing

Start your program by reading from standard in and splitting the input string on spaces. To test your program echo the result to standard out.

We will leverage the strtok standard library call for this part of the assignment. Below is a basic example (modified from cplusplus.com):

We will also leverage the fgets standard library call to read input from standard-in.

Smash built-in commands

Once you have your program reading from standard in you will add in a few basic commands and call your first system call! A shell will typically farm out 99% of the work to other programs. There are some things such as cd and exit that are built into the shell. We will be implementing a few built in commands before we start to fork/exec.

The Exit command

Your shell should exit when the user types in exit. You will need to use strcmp to compare the string literal "exit" with the user input.

The cd command

When a user types cd some/directory you will need to use the Linux system call chdir to change the directory of the current process (your smash process) to the given directory. If the user inputs an invalid directory, you need to output the string error: some/directory does not exist. Your shell will replace some/directory with the string the user input.

In order to determine if the chdir() command worked you will need to use the getcwd system call to get the current working directory after the cddir() command. Print the result to ensure that your directory did change. NOTE: Read the documentation for getcwd very carefully! You do NOT need to use malloc for this part of the project we are going to leverage a GNU extension to the POSIX.1-2001 standard that allows us to avoid malloc.

Your should print an error under the following conditions:

chdir() returned non-zero

getcwd() returned an unknown result

If the user enters any other command your shell should print the tokens in the format specified below. The $ is your smash prompt and will be hard coded for this part of the assignment. NOTE: Make sure and print the $ to stderr NOT stdout (use fprintf for this).

Example output

Explanation / Answer

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <ctype.h>

//This method looks up the input command against the lookup for
//validity purposes
bool FunctionLookUp(char *argv, char *Lookup[])
{
   bool match = false;
   for(int i = 0; i < 10; i++){
       if(*Lookup[i] == *argv){
           match = true;
           break;}
   }
   return match;
}
void parse(char *line, char **argv)
{
     while (*line != '') {       /* if not the end of line ....... */
          while (*line == ' ' || *line == ' ' || *line == ' ')
               *line++ = '';     /* replace white spaces with 0    */
          *argv++ = line;          /* save the argument position     */
          while (*line != '' && *line != ' ' &&
                 *line != ' ' && *line != ' ')
               line++;             /* skip the argument until ...    */
     }
     *argv = '';                 /* mark the end of argument list */
}

bool CheckForwardSlash(char temp[], int size)
{
      bool present = false
      for(int i = 0; i < size; i++){
          if(temp[i] == '/'){
           present = true;
           break;
           }
      }
      return present;
}
void execute(char **argv)
{
     pid_t pid;
     int    status;
   
     if ((pid = fork()) < 0) {     /* fork a child process           */
          printf("*** ERROR: forking child process failed ");
          exit(1);
     }
     else if (pid == 0) {          /* for the child process:         */
          if (execvp(*argv, argv) < 0) {     /* execute the command */
               printf("*** ERROR: exec failed ");
               exit(1);
          }
     }
     else {                                  /* for the parent:      */
          while (wait(&status) != pid)       /* wait for completion */
               ;
     }
}


void main(void)
{
     char line[1024];                         // The input line               
     char *argv[3];                           // An array of pointers to the tokens (the tokens were parsed
                                               // from the input line)
     bool check1, check2 = false;

   //A look-up table that contains some of the valid commands
   char *LookUp[11] = {"emacs", "kill", "bye", "jobs", "fg", "chmod", "cd", "help", "cat", "cp"};
   
     while (1) {                              // repeating....       
          printf("tish >> ");                 // the prompt           
          gets(line);                         // read in the command line   
          printf(" ");
          parse(line, argv);                  // parse the line
     
          if (strcmp(argv[0], "bye") == 0)    // exit if the user enters bye
               {exit(0);}

                      //Input Validation//
                      ///////////////////

          //If the input is just a white space character, continue with the next iteration of the loop
          if (isspace(argv[0]))
           continue;    //If it is some sort of a white-space character,
                       //skip the current iteration of the while loop
  
          
          //Call a function that checks for the validity of the input command
          check2 = FunctionLookUp(argv, LookUp);
          if(check2 == false){
              fprintf("Invalid Command ");
              continue;
          }

          //Test argv[0] for invalid internal commands. Check for Letters and Negative numbers.
          if(strcmp(argv[0], "kill") == 0){
           if(isaplha(argv[1]) || atoi(argv[1]) < 0){
           fprintf("Invald PID entered");
           fprintf("Enter a positive numeric number");
           continue;
           }
           }
         
                                                   
      
         int size = sizeof(argv[1]) + 1;
         char temp[size];
       strcpy(temp,agrv[1]);
         check1 = CheckForwardSlash(temp, size);
       //If true is returned by the CheckForwardSlash method, skip the rest of the while loop
         if(check1 == true){              
         printf("Invalid file format ");
         printf("Avoid Forward Slashes in your file name ");
         continue;
         }

                      //Input Validation Ends//
        ///////////////*********************//////////////////

       //Signals to catch the Ctrl-C and Ctrl/ combination
        signal(SIGINT, SIG_IGN);                   //The instructions said to ignore the SIGINT signal
        signal(SIGTERM, SIG_DFL);               //SIGTERM signal must be caught.
        execute(argv);                          //Finally, execute the command
     }
}

c program for shell

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#ifdef _WIN32
#include <windows.h>
#define chdir _chdir

#else
#include <unistd.h>
#endif

#define MAX_LENGTH 1024
#define DELIMS " "

int main(int argc, char *argv[]) {
char *cmd;
char line[MAX_LENGTH];

while (1) {
    printf("$ ");
    if (!fgets(line, MAX_LENGTH, stdin)) break;

    // Parse and execute command
    if ((cmd = strtok(line, DELIMS))) {
      // Clear errors
      errno = 0;

      if (strcmp(cmd, "cd") == 0) {
        char *arg = strtok(0, DELIMS);

        if (!arg) fprintf(stderr, "cd missing argument. ");
        else chdir(arg);

      } else if (strcmp(cmd, "exit") == 0) {
        break;

      } else system(line);

      if (errno) perror("Command failed");
    }
}

return 0;
}