In this MP, you will implement a simple Unix shell interpreter (e.g., Terminal, Konsole etc.) called Shell. The basic function of a shell is to accept commands as inputs and execute the corresponding programs in response.
The purpose of this MP is to help you learn the basics of system calls for creating and managing processes as you implement your Shell program. You will write the code for your Shell program in shell.c inside your mp2 directory. The two other files inside your mp2 directory: vector.c and vector.h provide the implementation of a generic data structure vector_t that you may use to implement the history feature of your Shell.
Your shell prompt MUST use the following format:
Shell (pid = x)y}You can use printf("Shell (pid = %d)%d} ", x, y) to print out this prompt. Here x is the current process ID and y is the command counter. Use getpid() to determine the process ID of the current process.
The Command Counter, y starts at 1 and is incremented after each command, regardless of whether the command succeeds or fails to execute. The only time when you should NOT increment y is when:
Read a line from standard input. This line will be your command.
The constant SHELL_BUFFER_SIZE, defined in shell.c, indicates the maximum length of a valid command. This means that the shell allows the user to enter a SHELL_BUFFER_SIZE characters long valid command string. Any command larger than this size should be rejected by your Shell and the shell should output the following error message (without the quotes): "Commands should be within the size of 256 characters"
Notes: While verifying the size of the command, Shell does not count the trailing new line character, '\n' or the null terminator, '\0' after each command to be a part of the buffer.
Parse the command line into an array of tokens. A token should consist of a sequence of non-whitespace characters separated from other tokens by whitespace characters.
The first token is the name of the command and the rest of the tokens are the arguments to that command name. By analyzing the command name, Shell determines the type of the command and executes it accordingly.
Shell supports two types of commands: built-in and non built-in. While built-in commands are executed without creating a new process, a non built-in command MUST create a new process to execute the program for that particular command.
Your Shell will support four built-in commands: cd, exit and two commands (!#N and !N) based on a history feature. Your Shell should save all commands (built-in or non built-in) issued during the current session. You should make no assumption about the size of the history.
cd xxx
Changes the Shell's current working directory to 'xxx'. [Hint: Relevant function chdir().]
If for some reason (for example, xxx is not a valid directory), the command "cd" does not end up successfully, In this case, Shell should retain its current working directory and should print (without the quotes): "Not valid" to indicate the error.
exit
Terminates the Shell.
!#N
Prints a list of the last "N" commands in the following format:
!1: ls -l
!2: pwd !3: ps
... !N: ps
where '!1' is the last command, '!2' is the second last command, and so on and so forth.
Notes:
!N
Re-executes the last N'th command from history. You may safely assume that '!N' will not be part of a larger command.
Before executing the N-th command, Shell MUST print out the recalled command to stdout. You must save the actual command executed in the history rather than "!N", eg. if "!1" re-executes command 'ls', you should store 'ls' as history rather than '!1'.
Shell(...)1} ls
Shell(...)2} !1
ls #re-executes ls
Shell(...)3} !1
ls #re-executes ls
Notes:
Tips: Flush the stdout after printing the recalled command (man fflush) just before executing the command to avoid potential output inconsistencies.
Note: For your convenience, we have provided the implementation of a data structure vector_t inside vector.c and vector.h. Feel free to use this data structure to implement the history feature for your Shell. The details of this data structure are provided at the end of this specification in the Appendix section.
Assumption:
If the command is not a Shell built-in (i.e., any command other than cd, exit, !#N and !N), Shell should consider the command name to be the name of a file that contains executable binary code. Such a code must be executed in a process different from the one executing the shell. You can use system() from stdlib.h to execute such a command. Sample codes are available on Linux's man page
Some non built-in UNIX commands that you may try to see whether your Shell works as it should are: ls, pwd, ps, echo hello.
To compile and run your shell, run the following commands from a Terminal on a Linux machine:
%> make clean
%> make
%> ./shell
Type "exit" to exit from Shell.
In CS 241, please include answers to:
We provide you with the implementation of a generic data structure: vector_t. The structure is defined inside vector.h and the functions supported are implemented inside vector.c. Please do NOT modify these two files. Otherwise, the autograder may fail to compile your Shell program. You may use vector_t to store the command history of your Shell. To use vector_t, just include the header file "vector.h" inside your Shell program shell.c. You may also implement your own data structures to keep track of the Shell history instead of using vector_t.
typedef struct _vector {
void **buffer;
unsigned int size;
unsigned int allocSize;
} vector_t;
Elements:
Functions:
void vector_init(vector_t *v)
Initializes the vector.
Parameter:
Assumption:
void vector_destroy(vector_t* v)
Frees the vector.
Parameter:
Assumption:
void vector_append(vector_t* v, void *item)
Appends an element to the end of the vector.
Parameter:
void *vector_at(vector_t* v, unsigned int idx)
Returns the element at the specified index in the vector.
Parameter:
Assumption:
unsigned int vector_size(vector_t* v)
Returns the number of elements in the vector.
Parameter:
Returns:
To enable better understanding of the vector_t data structure, we provide you with an example code that uses this data structure in testvector.c. Feel free to modify this tester. To make and run the tester for the data structure testvector.c, use:
%> make # make/compile your program
%> ./testvector # run the testvector program
Please fully read cs241.html for more details on grading, submission, and other topics that are shared between all MPs in CS 241.