// Do not edit this file

#ifndef _SHELL_H_
#define _SHELL_H_

// These libraries are the only ones that can be used

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define SHELL_PROMPT "thsh$ "

#define SUCCESS 0
#define ERROR -1

#define MAX_LINE_SIZE 1000
#define MAX_ARG_LEN 100
#define MAX_ENV_VAR_LEN getpagesize() * 32

/**
 * Represents a command
 *
 * For example, "cd /mnt/cdrom" would be parsed into {argc = 2,
 * argv={"cd", "/mnt/cdrom", NULL}}
 *
 * Assume each arg is at most MAX_ARG_LEN characters
 */
typedef struct {
    int argc;
    char** argv;
} command_t;

/**
 * Allocate memory for p_cmd->argv.
 *
 * Assume that when this function is called,
 * p_cmd->argc has been set to the correct value.
 *
 * The length of char** p_cmd->argv should be set to argc + 1.
 * p_cmd->argv[0] ... p_cmd->argv[argc - 1] are for the arguments.
 *
 * p_cmd->argv[argc] should be set to NULL.
 *
 * Also, for each char* in p_cmd->argv, malloc MAX_ARG_LEN chars.
 *
 * @param p_cmd command_t* to allocate memory for
 * @param argc
 * @return void
 */
void alloc_mem_for_argv(command_t* p_cmd);

/**
 * Free memory used by p_cmd. Specifically, free each char* in p_cmd->argv,
 * then free p_cmd->argv.
 *
 * To be safe, you should also set the pointer values to NULL after freeing.
 *
 * @param p_cmd
 * @return void
 */
void cleanup(command_t* p_cmd);

/**
 * Parses line (input from user) into command_t* p_cmd.
 *
 * Determine the number of arguments via string parsing.
 * Then, set p_cmd->argc to the correct value,
 * and allocate memory for p_cmd->argv.
 * Finally, ***copy*** the arguments from line into p_cmd->argv.
 *
 * When copying the arguments, do not simply assign pointers. You must
 * use str(n)cpy (or reimplement its logic) to copy the arguments.
 *
 * p_cmd is NULL when this function is called (see main.c).
 *
 * Example 1: If line is "cd /mnt/cdrom", then the fields in p_cmd should be
 * {argc = 2, argv = {"cd", "mnt/cdrom", NULL}}
 *
 * Example 2: If line is "ls -la", then the fields in p_cmd should be
 * {argc = 2, argv = {"ls, "-la", NULL}}
 *
 * Example 3: If line is NULL, then the fields in p_cmd should be {argc =
 * 0, argv = {NULL}}
 *
 * Example 4: If line is "   ls   -la   ", then the fields in p_cmd should be
 * the same as in example 2. Note the additional whitespace in the input.
 *
 * We recommend using strtok to handle this parsing.
 * https://systems-encyclopedia.cs.illinois.edu/articles/c-strtok/
 *
 * strtok's functionality is similar to that of split in Python or Java.
 * For example, in Python, we can do
 *
 * >>> "cd /mnt/cdrom".split(" ")
 * ['cd', '/mnt/cdrom']
 *
 * When used correctly, strtok will handle all of the above cases correctly.
 * Alternatively, you may reimplement tokenizing logic yourself, but be sure to
 * handle example 4 (extra whitespace) correctly.
 *
 * NOTE: strtok mutates the input char* (by inserting null terminators). So, to
 * be safe, you should use strtok on a clone of the input char*. You can create
 * a clone using strdup.
 *
 * @param line string input from user
 * @param p_cmd pointer to struct that will store the parsed command
 * @return void
 */
void parse(char* line, command_t* p_cmd);

/**
 * Determines if the executable in p_cmd (i.e., p_cmd->argv[0]) can be found
 * in one of the directories in $PATH. Returns true if so, else false.
 * If the executable is found in $PATH, then p_cmd->argv[0] is mutated to be
 * the absolute path of the executable.
 *
 * To see the format of $PATH, run `echo $PATH` in your terminal, which should
 * output something like
 * /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/mnt/learncli/bin
 *
 * For example, suppose the executable is "ls". This function needs to find its
 * absolute path in the file system. For the $PATH above,
 * it appends "/ls" to the first directory, resulting in
 * /usr/local/sbin/ls. However, this file does not exist, and same for the next
 * two directories. However, /usr/bin/ls exists, so p_cmd->argv[0] is set to
 * "/usr/bin/ls", and the function returns true.
 *
 * If the executable is "doesnotexist", then this function would not find that
 * file after checking all directories in $PATH, so it would return false.
 *
 * Note: This behaves similarly to the `which` command, e.g., `which ls`.
 *
 * To access $PATH within C, use getenv.
 * For example, char* path = getenv("PATH");
 *
 * To parse PATH, we recommend strtok, as described in the docstring for the
 * parse function.
 *
 * To check if a file exists, use access with amode F_OK
 * (https://pubs.opengroup.org/onlinepubs/009695299/functions/access.html).
 *
 * @param p_cmd
 * @return true (if command in PATH) | false (if command not in PATH). Remember
 * to mutate p_cmd->argv[0] to be the full path of the executable if it is in
 * PATH
 */
bool find_full_path(command_t* p_cmd);

/**
 * Execute a command
 *
 * When this function is called, p_cmd has already been parsed (see main.c)
 *
 * p_cmd may be a built-in command (cd or exit) or non-built-in
 *
 * Use is_builtin and do_builtin to detect and execute built-in commands
 *
 * For non-built-in commands, use find_full_path.
 * Also, use fork, execv, and wait.
 *
 * Do not use execlp, execvp, execvpe, or any other p variant that searches PATH
 * for you
 *
 * If a command cannot be found, print "Command {command} not found!\n".
 * For example, if the command "thisisnotacommand" is entered, then this
 * function should print "Command thisisnotacommand not found!\n"
 *
 * @param p_cmd
 * @return SUCCESS | ERROR
 */
int execute(command_t* p_cmd);

/**
 * Determines if p_cmd is a valid built-in command
 *
 * @param p_cmd
 * @return true (command is built-in) | false (command is not built-in)
 */
bool is_builtin(command_t* p_cmd);

/**
 * Executes built-in commands such as cd and exit
 *
 * @param p_cmd
 * @return SUCCESS | ERROR
 */
int do_builtin(command_t* p_cmd);

#endif