sourcecode

Wednesday, July 11, 2012

EggShell

CIS415 Spring 2012, Project1

Written in C

#define _POSIX_SOURCE
#define _BSD_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>//malloc
#include <stdio.h>
#include <errno.h>
#define SIZE 1024
char USAGE[]="Usage: $EggShell seconds";

int string_to_int(char* input);//convert a string to int. when encounter a non-number, the rest of the string is ignored
void print_prompt();//print the shell prompt
void print_int(int num);//print an integer to the monitor
void print_string(char* input);//print a string, ended with NULL, to the monitor
void print_cr();//print to monitor carrage return/new line
char** command_parser(char* input, int* output_length);//the input string is parsed by space, ' ', to substrings stored in output.
//the length of the string array returned: output_length is recorded. The caller is responsible to free the memory using command_parser_free()
void command_parser_free(char** string_array, int length);
int read_terminal(char* buf);//if the string is ended with a carrige return, common for termial input, it is parsed to end with 0.
//return the actually string length
int run_command(char** command,int time_out);
void alarm_handler(int signum);

/****************************************/
int main(int argc, char** argv){
  int time_out = 1;//second
  if (argc != 2){
    print_string(USAGE);
    print_cr();
    if (argc > 2) return 0;
  }
  else time_out = string_to_int(argv[1]);
  char read_buff[SIZE+1];
  read_buff[SIZE] = 0;
  while(1){
    print_prompt();
    int read_length = read(STDIN_FILENO, read_buff, SIZE);// read_terminal(read_buff);
    if (read_length && '\n'==read_buff[0]) continue;//only Enter is pressed
    if (read_length && '\n'==read_buff[read_length-1]) --read_length;
    read_buff[read_length] = 0;//terminate string
    int fork_id = fork();
    if (fork_id < 0){
      print_string("Fork failed!\n");
    }else if(fork_id > 0){//parent
      int status;
      wait(&status);//man wait for the use of status
      if(WIFSIGNALED(status)){
 print_string("All eggs are hatched! You need another shell!\n");
      }
      else if (WIFEXITED(status)){
        if (EXIT_EGGSHELL == WEXITSTATUS(status)){
   print_string("Exit\n");
   return 0;
 } else if (COMMAND_ERROR != WEXITSTATUS(status))
   print_string("You escaped, for now!\n");
      }
    }else{//child
      int vector_length = 0;
      char** string_vector = command_parser(read_buff, &vector_length);
      if (!string_vector || !string_vector[0]){
 _exit(EXIT_EGGSHELL);//input is ctrl+d only
      }
      run_command(string_vector, time_out);
      command_parser_free(string_vector, vector_length);
      //int errsv = errno;
      perror("Command Error");
      _exit(COMMAND_ERROR);
      //exit with a command error
    }//eles child
  }//for ii
}//main()
/***************************************/
int string_to_int(char* input){
  int result = 0;
  for(int ii = 0; input[ii] >= '0' && input[ii] <='9'; ++ii){
    result *= 10;
    result += input[ii]-'0';
  }//for ii
  return result;
}//string_to_int()

void print_prompt(){
  char prompt[]="$EggShell>";
  print_string(prompt);
}

void print_int(int num){
    if (!num) return;
    char digit = num % 10 + '0';
    print_int(num/10);
    write(STDOUT_FILENO,&digit,1);
}//print_int()

void print_string(char* input){
  if(!input) return;
  for(int ii = 0; input[ii] != 0; ++ii){
    write(STDOUT_FILENO,&input[ii],1);
  }//for ii
}//print_string()

void print_cr(){
  write(STDOUT_FILENO,"\n",1);
}//print_cr
char** command_parser(char* input, int* output_length){
  *output_length = 0;
  char** v_string = (char**)malloc(128*sizeof(char*));//max number of arguments
  for(int ii = 0; input[ii]; ){//whole string
    while (input[ii] == ' ') ++ii;
    if (!input[ii]) break;
    char* a_string = (char*)malloc(128*sizeof(char));//max length of an argument
    int jj = 0;//index for a word
    while ( input[ii] && input[ii] != ' '){
      a_string[jj++] = input[ii++];
    }
    a_string[jj++] = 0;//string end
    a_string = realloc(a_string, jj*sizeof(char*));//reszie (shrink) word string
    v_string[(*output_length)++] = a_string;//careful, ++ is higher than *
  }//for ii
  v_string[(*output_length)++] = NULL;
  v_string = realloc(v_string, *output_length*(sizeof(char*)));
  return v_string;
}//command_parser()
void command_parser_free(char** string_array, int length){
  for(int ii = 0; ii < length; ++ii) free(string_array[ii]);
  free(string_array);
}

int read_terminal(char* buf){
  int read_in = read(0, buf, SIZE);
  if (read_in &&  buf[read_in-1] == 10){//10 is new line feed
      --read_in;
  }
  buf[read_in] = 0;
  return read_in;
}

int run_command(char** command, int time_out){
  alarm(time_out);
  signal(SIGALRM, alarm_handler);
  return execve(command[0],command,NULL);
}

void alarm_handler(int signum){//p268
  kill(getpid(),SIGKILL);
}
/****************END********************/

No comments: