NOT FINISHED YET ⚠️

Hi, this project called minishell but the 's' is silent 👹.

This project is about creating a simple shell, developed by me (adbouras) and (eismail).

What is a shell?!

In computer science, a shell is a user interface that allows users to interact with the operating system. It can be command-line based or graphical, but it’s most commonly associated with command-line interfaces (CLIs). Here are some key points about shells:

  • Types of Shells:

    • Command-Line Shells: These include text-based interfaces where users type commands to perform tasks. Examples are: Bash (Bourne Again SHell): Commonly used on Linux and macOS.
      Zsh (Z Shell): An extended version of Bash with more features.
      PowerShell: A powerful shell for Windows with scripting capabilities.
      Command Prompt: The traditional Windows shell for command-line operations.
    • Graphical Shells: These provide a graphical user interface (GUI) for users to interact with the system. Examples include desktop environments like GNOME or KDE.
    • Usage: Shells are used for a variety of tasks, including file manipulation, program execution, and system management. They are particularly useful for developers and system administrators who need to perform repetitive tasks or automate workflows.
    • Customization: Many shells support customization through configuration files (like .bashrc or .zshrc), allowing users to set aliases, functions, and appearance settings.
  • Our shell should:

    • Display a prompt when waiting for a new command.
    • Have a working history.
    • Search and launch the right executable (based on the PATH variable or using a relative or an absolute path).
    • Avoid using more than one global variable to indicate a received signal. Consider the implications: this approach ensures that your signal handler will not access your main data structures.
    • Not interpret unclosed quotes or special characters which are not required by the subject such as (backslash) or ; (semicolon).
    • Handle (single quote) which should prevent the shell from interpreting the metacharacters in the quoted sequence.
    • Handle " (double quote) which should prevent the shell from interpreting the metacharacters in the quoted sequence except for $ (dollar sign).
    • Implement redirections:
      • < should redirect input.
      • > should redirect output.
      • << should be given a delimiter, then read the input until a line containing the delimiter is seen. However, it doesn’t have to update the history!
      • >> should redirect output in append mode.
    • Implement pipes (| character). The output of each command in the pipeline is connected to the input of the next command via a pipe.
    • Handle environment variables ($ followed by a sequence of characters) which should expand to their values.
    • Handle $? which should expand to the exit status of the most recently executed foreground pipeline.
    • Handle ctrl-C, ctrl-D and ctrl-\ which should behave like in bash.
    • In interactive mode:
      • ctrl-C displays a new prompt on a new line.
      • ctrl-D exits the shell.
      • ctrl-\ does nothing.
    • Your shell must implement the following builtins:
      • echo with option -n
      • cd with only a relative or absolute path
      • pwd with no options
      • export with no options
      • unset with no options
      • env with no options or arguments
      • exit with no options

I - Displaying the prompt & reading the command.

For displaying the prompt and getting the command from the terminal, we have the right to work with the function read_line() which does both actions at the same time.

	#define PROMPT "minishell $ "
 
	char	*rl;

	while (1)
	{
		rl = readline(PROMPT);
		add_history(rl);		// add rl to a working history.
		printf("%s\n", rl);
	}
	clear_history();			// frees the working history.

NOTICE: You may encounter some issues with the readline library. Please ensure you follow these steps:

  • Include the necessary tokensers in your code:
	#include <stdio.h>
	#include <readline/readline.h>
	#include <readline/history.h>
  • Add the following linker command to your Makefile: -lreadline
    If you continue to experience problems with the readline library, you may need to install it manually:
  • Intall homebrew.
  • Then, install the readline library using the following command
brew install readline
  • Ensure you link it in your Makefile by adding:
LDFLAGS = -L/Users/<login>/.brew/opt/readline/lib
INCLUDES = -I/Users/<login>/.brew/opt/readline/include

Now that we've read the command, the next step is to parse it. I’ve decided to proceed with lexing.

II - Lexer

A lexer, or lexical analyzer, is essential in converting a string or code into a sequence of tokens. Tokens are categorized strings or symbols that represent the fundamental components of a language, such as keywords, operators, and identifiers.

In essence, the lexer for this project is responsible for identifying and naming elements, such as recognizing "WORD" for a word, white space for a "W_SPACE" and "PIPE" for a pipe.

  • This is the structure that we went with:

     typedef struct s_data
     {
     	t_token	*tokens;
     	t_exec	*exec;
     }	t_data;
    • tokens: pointer to a linked list of tokens.
    • exec: pointer to execution linked list.
  • This is the tokens structure:

     typedef struct s_token
     {
     	char			*content;
     	int				len;
     	t_type			type;
     	t_state			state;
     	struct s_token	*next;
     	struct s_token	*prev;
     }	t_token;
    • content: pointer to a string holding an element.
    • len: content lenght.
    • type: structur identifing element type.
    • state: structur identifing element state.
    • next: pointer to the next element.
    • prev: pointer to the previous element.
  • Type structure:

     typedef enum e_type
     {
     	WORD = 0,
     	W_SPACE = ' ',
     	D_QUOTE = '\"',
     	S_QUOTE = '\'',
     	PIPE = '|',
     	REDIR_IN = '<',
     	REDIR_AND,
     	REDIR_OUT = '>',
     	REDIR_APP,
     	ENV = '$',
     }	t_type;
  • State structure:
    e_state is an identifier for a node satuse, if a node is inside a single or double quote, GENERAL is niether.

     typedef enum e_state
     {
     	IN_DQUOTE,
     	IN_SQUOTE,
     	GENERAL,
     }	t_state;