Back to Projects

minishell

A small shell program.

  • C
  • Makefile

Overview

This project is a recreation of the infamous linux shell. its a minimal version that allows the user to navigate through different files and directories, use any linux command or initialize any applications, contains pipes and redirections and here-documents.

only C was used in this project. visit the project for more details.

What I Built

My part of the project:

My part of this project included parsing the line, parameter expansion and building the abstract syntax tree that's crucial for the execution part;

Implementation details:

This implementation was built completely from scratch, with the exception of readline. i had a choice between recursive descent which is the one mostly used or a bottom-up approach, but i ended up with my own implementation that utilizes left-to-right reduction customized one after implementing both. since c doesn't have polymorphism, i had to find/invent a way to keep the project extensible just in case i wanted to add more features to my shell, so for that purpose i ended up discovering and using tagged unions.

an example of a use case would be like this:

#include <stdio.h>

typedef enum {
	EXPR_INT,
	EXPR_BINARY,
	EXPR_VARIABLE
} ExprType;

typedef enum {
	OP_ADD,
	OP_SUB,
	OP_MUL,
	OP_DIV
} BinaryOp;

typedef struct Expr Expr;

typedef struct {
	int value;
} ExprInt;

typedef struct {
	char *name;
} ExprVariable;

typedef struct {
	BinaryOp op;
	Expr *left;
	Expr *right;
} ExprBinary;

struct Expr {
	ExprType type;

	union {
		ExprInt integer;
		ExprVariable variable;
		ExprBinary binary;
	} as;
};

this way you could add up as many types as possible as long as they exist within the union all of you have to do is define that type within and utilize it.

The overall parsing goes through 3 main steps: 1. parsing a single like and tokenizing each word, as it makes sure that everything is valid 2. variable expasion by replacing the corresponding value from the environment variables. 3. the construction of the abstract syntax tree. 4. scanning over the syntax and detecting any potential lexical errors within the ast.

then the second part of the project which is related to execution, in that part we go through the entire AST as we fork out processes, initiate pipes when we need to and redirect inputs and outputs to the right directions.

project rules and limitations:

we were not allowed to utilize most of the c standard libraries, we mostly worked with "unistd.h" for syscalls, stdlib but just for malloc and free, and a couple of functions from a previous project lib42 "libft.h"

those limitations make the project much harder, since you are expected to implement everything rather than relying on other libraries, previous implementations and external dependencies and libraries. the only exception for this being the gnu readLine because of its complexity.

overall its a really intersting project to work with and I've learned a lot from it, especially about how bash works and also how programming languages are parsed, especially interpreted languages.

Goals:

the goal was not just an interactive shell introduction, but it was a deep dive into interpretation as well building an AST was not my only option but I've decided to take the chance to learn as much as i can from this project it also includes a lot of details about signal handling, pipes and forking, how a shell communicates with the kernel (ex: fork, execve, wait/waitpid, read/write) and so on. this project also happened to be my first group project, so learning how to navigate the project with multiple people working on it is a plus too.

Notes