Skip to content

zstenger93/minishell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📖 MINISHELL

A project to introduce you to depression

Hd Pepe Png Transparent Background



Allowed Functions

Used previously

Function Manual Page From lib Description
printf man 3 printf <stdio.h> write output to stdout
malloc man malloc <stdlib.h> allocate dynamic memory
free man 3 free <stdlib.h> free dynamic memory
read man 2 read <unistd.h> read from a file descriptor
write man 2 write <unistd.h> write to a file descriptor
open man 2 open <fcntl.h> open and possibly create a file
close man 2 open <unistd.h> close a file descriptor
fork man fork <unistd.h> create a child process
wait man wait <sys/wait.h> wait for process to change state
waitpid man waitpid <sys/wait.h> wait for process to change state
exit man exit <stdlib.h> cause normal process termination
execve man execve <unistd.h> execute program
dup man dup <unistd.h> duplicate a file descriptor
dup2 man dup2 <unistd.h> duplicate a file descriptor
pipe man pipe <unistd.h> create pipe
strerror man strerror <string.h> return string describing error number
errno man errno <errno.h> number of last error
access man access <unistd.h> checks the access permissions
perror man perror <stdio.h> look up the error message string corresponding to an error

New Functions

Function Manual Page From lib Description
getenv getting the value of variables from the env list
wait3 man wait3 <sys/wait.h> (obsolete) wait for process to change state, BSD style
wait4 man wait4 <sys/wait.h> (obsolete) wait for process to change state, BSD style
signal man signal <signal.h> ANSI C signal handling
kill man 2 kill <signal.h> send signal to a process
getcwd man getcwd <unistd.h> get current working directory
chdir man chdir <unistd.h> change working directory
stat man 2 stat <sys/stat.h> get file status by pathname
lstat man lstat <sys/stat.h> get file status by pathname (for symlinks)
fstat man fstat <sys/stat.h> get file status by fd
opendir man opendir <dirent.h> open a directory
readdir man readdir <dirent.h> read a directory
closedir man closedir <dirent.h> close a directory
isatty man isatty <unistd.h> determines if the file descriptor fd refers to a valid terminal
ttyname man ttyname <unistd.h> gets the related device name of a file descriptor for which isatty() is true
ttyslot man ttyslot <unistd.h> returns the name stored in a static buffer which will be overwritten on subsequent calls
ioctl man ioctl <sys/ioctl.h> manipulates the underlying device parameters of special files
unlink man unlink <unistd.h> deletes a name from the filesystem
sigaction man sigaction <signal.h> assigns an action for a signal specified by sig
sigemptyset man sigemptyset <signal.h> initializes a signal set to be empty
sigaddset man sigaddset <signal.h> adds the specified signal signo to the signal set
tcsetattr man tcsetattr <termios.h> setting the termios structure
tcgetattr man tcgetattr <termios.h> getting the termios structure
tgetent man tgetent <curses.h> <term.h> loads the entry for name
tgetflag man tgetflag <curses.h> <term.h> gets the boolean entry for id, or zero if it is not available
tgetnum man tgetnum <curses.h> <term.h> gets the numeric entry for id, or -1 if it is not available
tgetstr man tgetstr <curses.h> <term.h> returns the string entry for id, or zero if it is not available
tgoto man tgoto <curses.h> <term.h> instantiates the parameters into the given capability
tputs man tputs <curses.h> <term.h> applies padding information to the string str and outputs it
readline <stdio.h> <readline/readline.h> <readline/history.h> will read a line from the terminal and return it, using prompt as a prompt
rl_clear_history <stdio.h> <readline/readline.h> <readline/history.h> Clear the history list by deleting all of the entries
rl_on_new_line <stdio.h> <readline/readline.h> <readline/history.h> Tell the update functions that we have moved onto a new (empty) line, usually after outputting a newline
rl_replace_line <stdio.h> <readline/readline.h> <readline/history.h> Replace the contents of rl_line_buffer with text
rl_redisplay <stdio.h> <readline/readline.h> <readline/history.h> Change what’s displayed on the screen to reflect the current contents of rl_line_buffer
add_history <stdio.h> <readline/readline.h> <readline/history.h> Saving the line into the history

Our approach to the project

Input

Check for valid input, and if so we return our beautiful error message and how to use the program


Init

Initialize everything what's needed in normal mode

Or initialize the envless mode and extract the username


Signals

We have signals in the main loop for interactive mode

Signals in parent

Signals in child


Prompt

Normal mode

Arrow at the start which changes between green and red depending on if the cmd has been executed or not

Username which changes depending on the user

Space in between with colors

Folder name which always showing the current folder where you are

The green X sign at the end


No env

Works the same way except one thing which is the username

Since we don't have User in this case in out env

During the make I create a file with the makefile and echo the username to the file

Then when we launch the program instead of env, I read the username from the file :)

In case you try to delete the username from the file to break it, the prompt will change to a message

If you try to delete the file itself, it changes to another message as well ^^


Readline

After reading the input we trim the spaces from start-end and pass it to lexer

If the input is not empty, or not the exact same as the previous one then we store it in the history


Lexer

Since we kinda mixed lexer with the parser we have barely left with some checks at our lexer part

We look for wrong chars at line[0] like pipe or at the very end

Checks for wrong redirs right before pipes and if the last char is redir

A few other basic check, like empty imput or empty strings etc


Parser

First we did split with pipes int tokens and assign them to different types

One pipe is one command table

On each command table we have following

Command

Arguments

Redirections

Index

heredoc filename

**char for comamnd arguments


Executor

First we look for wrong redirections again and heredocs on the main process

If there is a wrong redirections, we execute heredocs until the wrong redir, after, not and delete the temp files

If no wrong redir, we execute heredocs the normal way and save all the file names in a **char

Now we can go pipe, fork and execute

Either execute a simple command without pipes

Or execute on the pipeline

In the child process

We handle redirections

Check for builtins

Then execute commands

Here we check for either direct path

Or we loop trough the PATH if the command is there or not

In the parent process

Waiting untill the last child process is going to finish and get it's exit status

Exec builtins to update structs/path if it is necessary


TO DO

Mandatory

Getting a headache from deciding who does what ✅

check for leaks ✅

builtins executed in child process ✅

delete unused code ✅

fix norm ✅

check redirections at start ✅

check how is awk being splitted for args ✅

assign the correct exit codes ✅

wexitstatus function for exit code ✅

fix segfault problems with first cmd ✅

set the executed variable for prompt at executions ✅

make sure special chars are being ignored or causing errors when its needed ✅

check thru while running wtihout env ✅

when no env, might store the user in struct and not in ENV ✅

execute builtins in child if they are called in pipes ✅

Using one global variable ✅

Be able to run without environment ✅

Have a working history ✅

Working relative or absolute PATH ✅

Check for unclosed quotes ✅

Handle ' and " so shell won't interpret with it's content ✅

Working main loop with readline ✅

Custom prompt with curr User and CWD ✅

Working redirections as:

< redirect output ✅

> redirect input ✅

<< (heredoc) but doesn't have to update the history ✅

>> should redirect in append mode to the file ✅

do not create or do anything in case of error in redirs ✅

only execute heredocs but only until the faulty redir and return prompt ✅

exapand variables in heredoc ✅

execute heredocs before anything else ✅

Handle environment variables with expander ✅

SIGNALS:

Handle ctrl-C, ctrl-D and ctrl-\ the way it works in bash:

ctrl + c displays a new prompt ✅

ctrl + d exits the shell ✅

ctrl + \ does nothing ✅

they shouldn't print anything on the line when received ✅

LEXER:

heredoc arg ✅

chechk valid operators ✅

tokenize the prompt using 4 different token type ✅

protect aginst valid special chars which aren't supposed to be handled ✅

splitting by pipes. one pipe = one table ✅

EXPANDER:

expand env variables ✅

only expand when we supposed to (' and ".....) ✅

Handle dollar+? (echo dollar+?) which should display the last exit code ✅

PARSER:

create the command table ✅

organize the table as follows:

command on *char ✅

arguments for the command with type and content option ✅

arguments converted to **char for execve ✅

redirections with type and content option ✅

check for edge, error cases and what we dont have to handle ✅

EXECUTOR:

turn builtins which accepted with uppercase letters as well into lowercase ✅

split redir args for execute ✅

get arguments on **char to pass for execve ✅

Execute simple cmd without pipes ✅

Execute on pipeline ✅

Implement the followiung BUILTINS:

echo with option -n ✅

cd with relative or absolute path ✅

pwd ✅

export ✅

unset ✅

env ✅

exit ✅

Bonus? 😅 🔫

implement (), && and || for priorities ❌

wildcards * should work for the current working directory ❌