CSC209 24s A3 CSCSHELL *

/*****************************************************************************/
/* CSC209-24s A3 CSCSHELL */
/* Copyright 2024 — Demetres Kostas PhD (aka Darlene Heliokinde) */
/*****************************************************************************/

#include “cscshell.h”

#define CONTINUE_SEARCH NULL

// COMPLETE
char *resolve_executable(const char *command_name, Variable *path){

if (command_name == NULL || path == NULL){
return NULL;

if (strcmp(command_name, CD) == 0){
return strdup(CD);

if (strcmp(path->name, PATH_VAR_NAME) != 0){
ERR_PRINT(ERR_NOT_PATH);
return NULL;

char *exec_path = NULL;

if (strchr(command_name, ‘/’)){
exec_path = strdup(command_name);
if (exec_path == NULL){
perror(“resolve_executable”);
return NULL;
return exec_path;

// we create a duplicate so that we can mess it up with strtok
char *path_to_toke = strdup(path->value);
if (path_to_toke == NULL){
perror(“resolve_executable”);
return NULL;
char *current_path = strtok(path_to_toke, “:”);

DIR *dir = opendir(current_path);
if (dir == NULL){
ERR_PRINT(ERR_BAD_PATH, current_path);
closedir(dir);

struct dirent *possible_file;

while (exec_path == NULL) {
// rare case where we should do this — see: man readdir
errno = 0;
possible_file = readdir(dir);
if (possible_file == NULL) {
if (errno > 0){
perror(“resolve_executable”);
closedir(dir);
goto res_ex_cleanup;
// end of files, break

if (strcmp(possible_file->d_name, command_name) == 0){
// +1 null term, +1 possible missing ‘/’
size_t buflen = strlen(current_path) +
strlen(command_name) + 1 + 1;
exec_path = (char *) malloc(buflen);
// also sets remaining buf to 0
strncpy(exec_path, current_path, buflen);
if (current_path[strlen(current_path)-1] != ‘/’){
strncat(exec_path, “/”, 2);
strncat(exec_path, command_name, strlen(command_name)+1);
closedir(dir);

// if this isn’t null, stop checking paths
if (possible_file) break;

} while ((current_path = strtok(CONTINUE_SEARCH, “:”)));

res_ex_cleanup:
free(path_to_toke);
return exec_path;

Command *parse_line(char *line, Variable **variables){
return NULL;

** This function is partially implemented for you, but you may
** scrap the implementation as long as it produces the same result.
** Creates a new line on the heap with all named variable *usages*
** replaced with their associated values.
** Returns NULL if replacement parsing had an error, or (char *) -1 if
** system calls fail and the shell needs to exit.
char *replace_variables_mk_line(const char *line,
Variable *variables){
// NULL terminator accounted for here
size_t new_line_length = strlen(line) + 1;
char markers[new_line_length];

// Commented so no warnings in starter make
// Variable *replacements = NULL;
// Variable **current = &replacements;
// const char *parse_var_st, *parse_var_end;
memset(markers, ‘-‘, sizeof(markers));
markers[sizeof(markers) – 1] = ‘\0’;

// Code to determine new length
// and list of replacements in order

char *new_line = (char *)malloc(new_line_length);
if (new_line == NULL) {
perror(“replace_variables_mk_line”);
return (char *) -1;
memset(new_line, ‘\0’, new_line_length);

// Fill in the new line using markers and replacements

return new_line;

void free_variable(Variable *var, uint8_t recursive){