Creating a Terminal shell - Adding Builtins

This post is in continuation to to our series on creating a terminal shell. You can checkout the first post here. In this post, I’ll show how to add builtin commands to our shell.

Specifically, we’ll be tackling the cd builtin (A command provided by the shell). Now first let’s clear the air, why the heck is cd not a program ? So what is the primary purpose of the command cd ? To change the current working directory right ? But for whom if I may ask ? Well, in this case the terminal shell itself. So, if cd were to be a program (which it is in some POSIX compliant systems), whenever we run cd, it would change the current working directory of the cd process itself, and when the process would terminate, and return to shell, the shell would still have the same current working directory

This is how it would look :

shell $ pwd
/home/blog
shell $ cd_process /some/other/directory
    cd_process starts
        chdir (/some/other/directory) - System Call
        current working directory change successful
    cd_process terminates
shell $ pwd
/home/blog

Therefore, to get around this conundrum, the shell itself has call the chdir system call to be able to change its current working directory. The code for doing that is quite simple :

Yes, that’s it. Now just for the sake of it, and to make it easier to add more builtins in the future, we’ll add some code to our existing shell to figure out whether it’s dealing with a builtin command or some other program.

First we’ll create a new header file inside our include directory builtin.h which will house the declarations for common functions, and also declare an enum which will correspond to all the builtins we’ll add to our shell. Right now it’ll have only two entries, one for cd, and one for null check.

Now we’ll create a series of utility functions which will help in translating the string “cd” that we’ll get from command line to the enum BLTIN_CD, to the actual function to be called. These functions are not really necessary right now, but it really helps to create them right in the beginning. In the future, when you add more builtin commands to your shell, it will be way more easier to do so.

The first function in our list takes a command string and returns an enum, this is the easiest of the bunch, we just compare the input string to predefined string list and return the corresponding enum.

Now the next function is getting the actual function pointer from the builtin enum. Now don’t get scared of the syntax you’re going to see below. A function pointer is just a pointer to a function, and using that pointer we can call functions. Now there is a weird way to write functions which return a function pointer, I never remember it, just google when need to :P

Now the only function left is calling our function through the function pointer, which is not very difficult.

In the end we’ll have the following file, save this as src/builtins.c

Now we just need to call our function inside our eval function. (Don’t forget to include builtins.h)

The final thing to do is change our Makefile to start building our builtins.c file. Just add $(LIB_DIR)/builtins.o to the OBJS variable

OBJS=$(LIB_DIR)/shell.o $(LIB_DIR)/builtins.o

Now just build, and we can cd into directories

kartik@kt:~/projects/blog$ ./bin/msh
msh >> pwd
/hdd/projects/blog
msh >> cd /
msh >> pwd
/

In the next post we’ll add the support for redirecting streams, so hold on!