Lesser Program 2 - The Shell
You are about to set out on a project/fool's errand to write an operating system. Most of your time is going to be spent writing the kernel, scheduler, memory maps, device drivers, and all kinds of elegant and slick code to glue it all together.
However, without a program to run on it, all this will be basically meaningless! You should provide your users with at least a command shell and a few small programs for that shell to spawn. We will write a few of these "lesser" programs as time goes on, and we'll start with the most difficult one first, the shell.
I know what you're thinking. Start with the most difficult?! Well, remember you haven't started on your OS project in earnest yet, and so I assume you have plenty of free time. Rather than have you moping about your dorm or wandering aimlessly around campus, I feel it my duty to give you a task to keep you in front of a computer where you won't bother anyone. So here goes!
A command shell is not a special program. It is a standard user space process which uses syscalls to perform its work. The commands you type into a shell fit into one of two categories:
- Built-in commands.
- Programs to Run.
Real modern shells have a lot of built-in commands. In fact, all modern shells are full, Turing Complete, programming languages. Calm down though, I'm not asking you to do anything that complex!
When you construct your shell you'll basically have two parts to it. The first is the command parser which will take a command line and and break it up into its constituent parts. It will determine whether each command specified in the command line is built-in or if it is a special character or a user program. The second part of the shell is the command executer. This will take the command from the shell and execute it.
So how much of a shell should you write? The answer is that you are going to implement a shell which basically matches the original v6 shell. The original v6 shell was a very simple shell by today's standards. It was only 898 lines of code!
You can see the original shell program by logging in to our simulated pdp11 and looking at the file /usr/source/s2/sh.c. Or, if you are lazy, you could just look at it online at http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/s2/sh.c This is written in old-school C which, like everything else from the 1970's, has wood panelling on it.
Now, one way you could satisfy this assignment is convert the old sh into modern c. That would be at least twice as hard as what I am asking you to do, but if that's what you want go for it!
What I want you to do is produce a subset of this shell. The old sh had a few bits of primitive job control that we are not going to implement. So here's what I want you to create a shell with two built-in commands:
- chdir - change to the specified directory
- ls - list the directory.
You'll note that this shell uses chdir, not cd! This is the way v6 worked. It was not until v7 that they had the bright idea of shortening it up. Furthermore, in the original shell ls was not a built in command. You can see the source code of the original ls at http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/s1/ls.c or on the pdp11 simulator in the file /usr/source/s1/ls.c.
In addition to this, your program should also handle the following special characters:
> redirect output to a file < redirect input from a file | pipe the output of the left program through the second
Keep in mind that pipelines can be multiple lines long
ls -l | grep c | grep rw
For simplicity though you can assume that a single line will contain a single command (or it will be blank).
The rest of the syntax is that arguments are separated by spaces. Sometimes arguments are separated by multiple spaces. Play around with the shell on a Unix system to get a feel for how it processes command lines.
You could, of course, also look at the equivalent programs in xv6. Reading the source will help you produce your own shell!
The spirit of the thing is to write your own, but if you feel like adapting sections of other shells, then go for it.
A Note About Library Functions
Your shell will eventually run in your OS. This means that it will basically need to be a stand-alone program with nothing but itself and the OS you are writing, which in turn means that you should not use any standard library functions. Instead, the only library functions you are allowed to use are those from section 2 of the Unix manual. And in fact, you'll want to be a little more constrained than that. Use only the syscalls specified in the xv6 list because that is all your OS will actually have.
Enjoy, and good luck!