MINISHELL ALGORITHM - PSEUDO CODE
==================================
MAIN PROGRAM FLOW:
------------------
FUNCTION main(argc, argv, env):
INITIALIZE mini structure
SET mini.in = duplicate(STDIN)
SET mini.out = duplicate(STDOUT)
SET mini.exit = FALSE
SET mini.ret = 0
CALL reset_fds(mini)
CALL env_init(mini, env)
CALL secret_env_init(mini, env)
CALL increment_shell_level(mini.env)
WHILE mini.exit == FALSE:
CALL sig_init()
CALL parse(mini)
IF mini.start != NULL AND check_line(mini, mini.start):
CALL minishell(mini)
CALL free_token(mini.start)
END WHILE
CALL free_env(mini.env)
CALL free_env(mini.secret_env)
RETURN mini.ret
END FUNCTION
PARSING ALGORITHM:
------------------
FUNCTION parse(mini):
SET signal handlers for SIGINT and SIGQUIT
IF mini.ret != 0:
PRINT "🤬 "
ELSE:
PRINT "😎 "
PRINT "minishell ▸ "
READ line from stdin using get_next_line()
IF EOF received:
SET mini.exit = TRUE
PRINT "exit"
RETURN
UPDATE mini.ret based on signal status
IF quote_check(mini, line) returns error:
RETURN
SET line = space_line(line) // Add spaces around separators
IF line starts with '$':
MARK first character for expansion
SET mini.start = get_tokens(line)
CALL free(line)
CALL squish_args(mini)
FOR each token in mini.start:
IF token.type == ARG:
CALL type_arg(token, 0)
END FOR
END FUNCTION
TOKENIZATION ALGORITHM:
-----------------------
FUNCTION get_tokens(line):
SET prev = NULL
SET next = NULL
SET i = 0
SKIP initial whitespace
WHILE line[i] exists:
SET sep = ignore_sep(line, i)
SET next = next_token(line, &i)
LINK next.prev = prev
IF prev exists:
LINK prev.next = next
SET prev = next
CALL type_arg(next, sep)
SKIP whitespace
END WHILE
IF next exists:
SET next.next = NULL
FIND and return first token
END FUNCTION
FUNCTION next_token(line, i):
ALLOCATE new token
SET j = 0
SET quote_char = ' '
WHILE line[i] exists AND (line[i] != ' ' OR inside quotes):
IF quote_char == ' ' AND line[i] is quote:
SET quote_char = line[i]
INCREMENT i
ELSE IF quote_char != ' ' AND line[i] == quote_char:
SET quote_char = ' '
INCREMENT i
ELSE IF line[i] == '\' (escape character):
INCREMENT i
ADD line[i] to token.str[j]
INCREMENT i, j
ELSE:
ADD line[i] to token.str[j]
INCREMENT i, j
END WHILE
SET token.str[j] = '\0'
RETURN token
END FUNCTION
EXECUTION ALGORITHM:
--------------------
FUNCTION minishell(mini):
SET token = next_run(mini.start, NOSKIP)
IF first token is redirection type:
SET token = mini.start.next
WHILE mini.exit == FALSE AND token exists:
SET mini.charge = TRUE
SET mini.parent = TRUE
SET mini.last = TRUE
CALL redir_and_exec(mini, token)
CALL reset_std(mini)
CALL close_fds(mini)
CALL reset_fds(mini)
WAIT for child process to complete
GET exit status
UPDATE mini.ret if this was last command
IF this is child process:
FREE memory and exit
SET mini.no_exec = FALSE
SET token = next_run(token, SKIP)
END WHILE
END FUNCTION
REDIRECTION AND EXECUTION ALGORITHM:
------------------------------------
FUNCTION redir_and_exec(mini, token):
SET prev = prev_sep(token, NOSKIP)
SET next = next_sep(token, NOSKIP)
SET pipe = 0
IF prev.type == TRUNC:
CALL redir(mini, token, TRUNC)
ELSE IF prev.type == APPEND:
CALL redir(mini, token, APPEND)
ELSE IF prev.type == INPUT:
CALL input(mini, token)
ELSE IF prev.type == PIPE:
SET pipe = minipipe(mini)
IF next exists AND next.type != END AND pipe != 1:
RECURSIVELY CALL redir_and_exec(mini, next.next)
IF (prev.type == END OR prev.type == PIPE OR prev == NULL)
AND pipe != 1 AND mini.no_exec == FALSE:
CALL exec_cmd(mini, token)
END FUNCTION
COMMAND EXECUTION ALGORITHM:
----------------------------
FUNCTION exec_cmd(mini, token):
IF mini.charge == FALSE:
RETURN
SET cmd = cmd_tab(token) // Convert tokens to string array
FOR each string in cmd:
SET cmd[i] = expansions(cmd[i], mini.env, mini.ret)
END FOR
IF cmd[0] == "exit" AND no pipe:
CALL mini_exit(mini, cmd)
ELSE IF is_builtin(cmd[0]):
SET mini.ret = exec_builtin(cmd, mini)
ELSE IF cmd exists:
SET mini.ret = exec_bin(cmd, mini.env, mini)
CALL free_tab(cmd)
CLOSE pipe file descriptors
SET mini.charge = FALSE
END FUNCTION
VARIABLE EXPANSION ALGORITHM:
-----------------------------
FUNCTION expansions(arg, env, ret):
CALCULATE new_arg_len = arg_alloc_len(arg, env, ret)
ALLOCATE new_arg with new_arg_len + 1
SET i = 0, j = 0
WHILE i < new_arg_len AND arg[j] exists:
WHILE arg[j] == EXPANSION:
INCREMENT j
IF arg[j] == '\0' OR not alphanumeric AND arg[j] != '?':
ADD '$' to new_arg[i]
INCREMENT i
ELSE:
CALL insert_var(expansion_struct, arg, env, ret)
END WHILE
SET new_arg[i] = arg[j]
INCREMENT i, j
END WHILE
SET new_arg[i] = '\0'
RETURN new_arg
END FUNCTION
BUILT-IN EXECUTION ALGORITHM:
-----------------------------
FUNCTION exec_builtin(args, mini):
SET result = 0
SWITCH args[0]:
CASE "echo":
SET result = ft_echo(args)
CASE "cd":
SET result = ft_cd(args, mini.env)
CASE "pwd":
SET result = ft_pwd()
CASE "env":
CALL ft_env(mini.env)
CASE "export":
CALL ft_export(args, mini.env, mini.secret_env)
CASE "unset":
CALL ft_unset(args, mini)
END SWITCH
RETURN result
END FUNCTION
EXTERNAL COMMAND EXECUTION ALGORITHM:
-------------------------------------
FUNCTION exec_bin(args, env, mini):
FIND PATH environment variable
IF PATH not found:
RETURN magic_box(args[0], args, env, mini)
SPLIT PATH by ':' into bin array
FOR each directory in bin:
SET path = check_dir(bin[i], args[0])
IF path found:
BREAK
END FOR
IF path found:
RETURN magic_box(path, args, env, mini)
ELSE:
RETURN magic_box(args[0], args, env, mini)
END FUNCTION
FUNCTION magic_box(path, args, env, mini):
SET pid = fork()
IF pid == 0 (child process):
CONVERT env to string array
IF path contains '/':
CALL execve(path, args, env_array)
HANDLE error and exit
ELSE (parent process):
WAIT for child process
RETURN exit status
END FUNCTION
PIPE HANDLING ALGORITHM:
------------------------
FUNCTION minipipe(mini):
CREATE pipe file descriptors
SET pid = fork()
IF pid == 0 (child process):
CLOSE write end of pipe
REDIRECT pipe read end to stdin
SET mini.pipin = pipe read fd
SET mini.parent = FALSE
RETURN 2
ELSE (parent process):
CLOSE read end of pipe
REDIRECT pipe write end to stdout
SET mini.pipout = pipe write fd
SET mini.last = FALSE
RETURN 1
END FUNCTION
REDIRECTION ALGORITHM:
---------------------
FUNCTION redir(mini, token, type):
CLOSE current output fd
IF type == TRUNC:
OPEN token.str for write with truncate
ELSE:
OPEN token.str for write with append
IF open failed:
PRINT error message
SET mini.ret = 1
SET mini.no_exec = TRUE
RETURN
REDIRECT stdout to file
END FUNCTION
FUNCTION input(mini, token):
CLOSE current input fd
OPEN token.str for read
IF open failed:
PRINT error message
SET mini.ret = 1
SET mini.no_exec = TRUE
RETURN
REDIRECT stdin to file
END FUNCTION
SIGNAL HANDLING ALGORITHM:
--------------------------
FUNCTION sig_int(code):
IF g_sig.pid == 0 (at prompt):
PRINT newline and new prompt
SET g_sig.exit_status = 1
ELSE (command running):
PRINT newline
SET g_sig.exit_status = 130
SET g_sig.sigint = 1
END FUNCTION
FUNCTION sig_quit(code):
IF g_sig.pid != 0 (command running):
PRINT "Quit: " + signal number
SET g_sig.exit_status = 131
SET g_sig.sigquit = 1
ELSE:
CLEAR display artifacts
END FUNCTION
MEMORY MANAGEMENT ALGORITHM:
----------------------------
FUNCTION free_token(start):
WHILE start exists:
SET next = start.next
FREE start.str
FREE start
SET start = next
END WHILE
END FUNCTION
FUNCTION free_env(env):
WHILE env exists:
SET next = env.next
FREE env.value
FREE env
SET env = next
END WHILE
END FUNCTION
TIME COMPLEXITY: O(n) where n is the length of input
SPACE COMPLEXITY: O(n) for token storage and environment variables