cmd
cmd > file
cmd < file
cmd | cmd
where "file" is any valid unix filename, and "cmd" is a program name
followed by zero or more arguments, with no wildcards, no in-line
command evaluation, no macro substitutions, and which does not
include the symbols ">", "<", or "|". For the special case when
"cmd" is "exit", your shell should terminate. Your shell should also be able to parse and execute command lines as above ending with an "&" that puts the specified process or processes in the background.
cmd &
cmd > file &
cmd < file &
cmd | cmd &
You can assume that all command lines are of the form described
here, and you do not have to check for any other sorts of input. In
particular, you do not have to expand filename wildcards,
do multi-step pipes, or do both input and output redirection on one
line. Use fork() and either execve() or execvp() to execute the commands. In Minix and Linux execve() is a systems call, and execvp() a library routine; on the SGIs, both seem to be systems calls. The main difference is that execvp() will search your current PATH for the specified executable, and doesn't have an explicit environment argument.
Your shell should prompt the user for each command line with the string "mysh > ". The final version you submit should not print any test messages or other extraneous jabber. You should print errors returned by execvp() or execve(); you can use strerror(errono) to make your error reporting more informative; errno is an external variable set to the most recent error, and strerror() translates this to a human-readable string. You may find the Unix string manipulation library routines convenient for parsing the command line; do a "man string" to learn more about these functions.
To redirect input from a file to a program, so the file appears as "standard input" to the program (e.g., as in the command "wc < test.dat"), you will have to manipulate file descriptors. To connect a file to the Unix "standard input" (by convention file descriptor 0), you can use the dup2() system call, as in the following example.
if ((fd=open(fname, O_RDONLY)) == -1) {
fprintf(stderr, "mysh error: can't open %s\n", fname);
exit(1);
}
dup2(fd, 0);
close(fd);
if (execvp(cmd, args) == -1)
fprintf(stderr, "mysh error: %s\n", strerror(errno));
If we wanted to execute the command "ls -l", "cmd" in the above
example would be the string "ls", and the string array "args" would
have its first element pointing to "ls", its second element pointing
to "-l", and its third element the null pointer 0, indicating the
end of the args array. The "&" function is easy to implement--without the "&", the shell waits for the child process; with the "&" it does not wait. The pipe function "|" is a little more difficult, and can be implemented either with a temporary file, or preferably or with a real Unix pipe.
The points for functionality are divided up as follows: 20% for simple commands, 20% for working i/o redirection, 20% for being able to successfully put a process in the background, and 20% for working pipes.
Do not use the system() sys call or invoke the unix shell to implement your shell!
You must do the project described here. Doing some other similar or dissimilar project, no matter how difficult or clever, may be worth 0 points. This is not a group project; please do your own work, and be careful about sharing your code. It is OK to discuss design issues, but in your documentation you should give credit to your sources.
submit cs421_0101 proj1 mysh.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>