project-Rushilwiz/shell.c

166 lines
4.5 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, " ");
}
}
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) {
if (is_builtin(p_cmd)) {
return do_builtin(p_cmd);
}
if (!find_full_path(p_cmd)) {
printf("Command %s not found!\n", p_cmd->argv[0]);
return ERROR;
}
// start fork
pid_t pid = fork();
if (pid < 0) {
return ERROR;
} else if (pid == 0) {
// child process
execv(p_cmd->argv[0], p_cmd->argv);
exit(EXIT_FAILURE);
} else {
// parent process
int status;
waitpid(pid, &status, 0);
}
return SUCCESS;
}
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;
}
}