mirror of
https://github.com/Comp211-SP24/project-Rushilwiz.git
synced 2025-04-07 22:10:20 -04:00
179 lines
5.6 KiB
C
179 lines
5.6 KiB
C
// PID: 730677144
|
|
// I pledge the COMP 211 honor code.
|
|
|
|
// All necessary libraries are included in shell.h
|
|
#include "shell.h"
|
|
|
|
void alloc_mem_for_argv(command_t* p_cmd) {
|
|
p_cmd->argv = (char**)malloc((p_cmd->argc + 1) * sizeof(char*));
|
|
for (int i = 0; i < p_cmd->argc; i++) {
|
|
p_cmd->argv[i] = (char*)malloc(MAX_ARG_LEN * sizeof(char));
|
|
}
|
|
|
|
p_cmd->argv[p_cmd->argc] = NULL;
|
|
}
|
|
|
|
void cleanup(command_t* p_cmd) {
|
|
for (int i = 0; i < p_cmd->argc; i++) {
|
|
free(p_cmd->argv[i]);
|
|
p_cmd->argv[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
char* line_copy = strdup(line);
|
|
char* argc_token = strtok(line_copy, " ");
|
|
p_cmd->argc = 0;
|
|
|
|
while (argc_token != NULL) {
|
|
p_cmd->argc++;
|
|
argc_token = strtok(NULL, " ");
|
|
}
|
|
|
|
alloc_mem_for_argv(p_cmd);
|
|
|
|
line_copy = strdup(line);
|
|
char* argv_token = strtok(line_copy, " ");
|
|
|
|
for (int i = 0; i < p_cmd->argc; i++) {
|
|
strcpy(p_cmd->argv[i], argv_token);
|
|
argv_token = strtok(NULL, " ");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
char* path_tokens = strtok(getenv("PATH"), ":");
|
|
|
|
while (path_tokens != NULL) {
|
|
char* full_path = (char*)malloc(MAX_ARG_LEN * sizeof(char));
|
|
strcpy(full_path, path_tokens);
|
|
strcat(full_path, "/");
|
|
strcat(full_path, p_cmd->argv[0]);
|
|
|
|
if (access(full_path, F_OK) == 0) {
|
|
strcpy(p_cmd->argv[0], full_path);
|
|
free(full_path);
|
|
return true;
|
|
}
|
|
|
|
free(full_path);
|
|
path_tokens = strtok(NULL, ":");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int execute(command_t* p_cmd) {
|
|
// TODO:
|
|
return 0;
|
|
}
|
|
|
|
bool is_builtin(command_t* p_cmd) {
|
|
// Do not modify
|
|
char* executable = p_cmd->argv[0];
|
|
if (strcmp(executable, "cd") == 0 || strcmp(executable, "exit") == 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int do_builtin(command_t* p_cmd) {
|
|
// Do not modify
|
|
if (strcmp(p_cmd->argv[0], "exit") == 0) {
|
|
exit(SUCCESS);
|
|
}
|
|
|
|
// cd
|
|
if (p_cmd->argc == 1) { // cd with no arguments
|
|
return chdir(getenv("HOME"));
|
|
} else if (p_cmd->argc == 2) { // cd with 1 arg
|
|
return chdir(p_cmd->argv[1]);
|
|
} else {
|
|
fprintf(stderr, "cd: Too many arguments\n");
|
|
return ERROR;
|
|
}
|
|
}
|