I’m stuck on Stage #VZ4
I’ve tried commenting out all extra error messages I had initially added, but somehow when the test calls “ls” which is not a built in shell command it spits out an error message that is specific to “cd” which is built in command. It also spits the error to stdout instead of stderr which makes it fail the test.
Here are my logs:
[compile] Compilation successful.
Debug = true
[tester::#VZ4] Running tests for Stage #VZ4 (Redirection - Redirect stderr)
[tester::#VZ4] [setup] export PATH=/tmp/raspberry/apple/banana:$PATH
[tester::#VZ4] Running ./your_program.sh
[tester::#VZ4] [setup] echo -n "apple" > "/tmp/baz/apple"
[your-program] $ ls -1 nonexistent 2> /tmp/qux/foo.md
[your-program] ls: /tmp/qux/foo.md: No such file or directory
[tester::#VZ4] Expected prompt ("$ ") but received "ls: /tmp/qux/foo.md: No such file or directory"
[your-program] ls: nonexistent: No such file or directory
[your-program] $
[tester::#VZ4] Assertion failed.
[tester::#VZ4] Test failed
And here’s a snippet of my code:
std::unordered_set<std::string> builtIn = {"echo", "exit", "type", "pwd", "cd"};
std::unordered_set<char> escapedChars = {'\\', '$', '\"', '\n'};
std::unordered_set<std::string> outRedirect = {">", "1>"};
std::vector<std::string> pathDirs;
std::string currentDir;
int stdOut, stdError;
bool out = false, error = false;
std::vector<std::string> tokenizeInput(const std::string& input) {
bool singleQuote = false, doubleQuote = false;
std::vector<std::string> tokens;
std::string token;
for (size_t i = 0; i < input.size(); i++) {
char c = input[i];
if (c == ' ' && !singleQuote && !doubleQuote) {
if (token == "2>") error = true;
else if (!token.empty() && outRedirect.find(token) == outRedirect.end() && !out) tokens.push_back(token);
else if (outRedirect.find(token) != outRedirect.end()) out = true;
else if (out && !token.empty()) handleOutputRedirection(token);
else if (error && !token.empty()) handleErrorRedirection(token);
token.clear();
} else if (c == '\'' && !doubleQuote) {
singleQuote = !singleQuote;
} else if (c == '\"' && !singleQuote) {
doubleQuote = !doubleQuote;
} else if (c == '\\') {
if (singleQuote) {
token += c;
} else if (doubleQuote) {
if (i+1 < input.size() && escapedChars.find(input[i+1]) != escapedChars.end()) {
token += input[++i];
} else {
token += c;
}
} else if (i+1 < input.size()) {
token += input[++i];
}
} else token += c;
}
if (!token.empty() && !out) tokens.push_back(token);
else if (!token.empty()) handleOutputRedirection(token);
return tokens;
}
void handleOutputRedirection(std::string file) {
int fd = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
// perror("open");
return;
}
dup2(fd, STDOUT_FILENO);
close(fd);
}
void handleErrorRedirection(std::string file) {
int fd = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
// perror("open");
return;
}
dup2(fd, STDERR_FILENO);
close(fd);
}
void closeErrorRedirect() {
dup2(stdError, STDERR_FILENO);
error = false;
}
void closeOutputRedirect() {
dup2(stdOut, STDOUT_FILENO);
out = false;
}
Code for cd and external commands:
void handleCd(const std::vector<std::string>& tokens) {
int status = 0;
if (tokens.size() == 1 || tokens[1][0] == '~') {
status = chdir(getenv("HOME"));
} else {
status = chdir(tokens[1].c_str());
}
if (status == -1) {
std::cerr << tokens[0] << ": " << tokens[1] << ": No such file or directory" << std::endl;
} else {
currentDir = std::filesystem::current_path();
}
}
void executeExternalCommand(const std::vector<std::string>& tokens) {
std::vector<char*> args(tokens.size() + 1);
for(size_t i = 0; i < tokens.size(); i++) {
args[i] = const_cast<char*>(strdup(tokens[i].c_str()));
}
args[tokens.size()] = nullptr;
pid_t pid = fork();
if (pid < 0) {
// perror("fork");
return;
} else if (pid > 0) { // Parent
int status;
if (waitpid(pid, &status, 0) == -1) {
// perror("waitpid");
return;
}
if (WIFEXITED(status) && WEXITSTATUS(status) == 127) {
std::cerr << tokens[0] << ": command not found" << std::endl;
} else if (WIFSIGNALED(status)) {
// std::cout << tokens[0] << ": terminated by signal " << WTERMSIG(status) << std::endl;
} else if (WIFEXITED(status) != 0 && WEXITSTATUS(status) != 0) {
// std::cout << tokens[0] << ": exit status " << WEXITSTATUS(status) << std::endl;
}
} else if (pid == 0) { // Child
execvp(tokens[0].c_str(), args.data());
// std::cerr << "Execution failed for command: " << tokens[0] << std::endl;
// perror("execvp");
exit(127);
}
for (size_t i = 0; i < tokens.size(); i++) {
free(args[i]);
}
}