basic implementation

-added exit, echo, type commands
-added ability to execute programs in the $PATH variable
This commit is contained in:
Joseph Aquino 2025-12-25 18:25:33 -05:00
parent e9651bce0d
commit f817fc9d7f
4 changed files with 155 additions and 4 deletions

View File

@ -3,6 +3,9 @@ project(shell)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
add_executable(shell src/main.cpp) add_executable(shell src/main.cpp
src/shellUtils.cpp
include/shellUtils.h)
target_link_libraries(shell PRIVATE readline) target_link_libraries(shell PRIVATE readline)
target_include_directories(shell PRIVATE include)

31
include/shellUtils.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <iostream>
#include <string>
#include <map>
#include <cstdlib>
#include <filesystem>
namespace sh
{
enum class Command
{
exit,
echo,
type,
unknown
};
inline const std::map<const std::string_view, const Command>commandMap
{
{"exit", Command::exit},
{"echo", Command::echo},
{"type", Command::type}
};
Command getCommand(std::string_view input);
std::optional<std::string> isExec(const std::string& input);
void printType(const std::string& input);
}

View File

@ -1,7 +1,46 @@
#include <iostream> #include <iostream>
#include <string>
#include <map>
#include <cstdlib>
#include <filesystem>
#include "shellUtils.h"
int main() int main()
{ {
std::cout << "Hello, World!" << std::endl; std::cout << std::unitbuf;
return 0; std::cerr << std::unitbuf;
}
bool exit {false};
while (!exit)
{
std::string input;
std::cout << std::unitbuf;
std::cout << "$ ";
std::getline(std::cin, input);
std::optional<std::string> result;
switch (sh::Command command = sh::getCommand(input))
{
case sh::Command::exit:
exit = true;
break;
case sh::Command::echo:
std::cout << input.substr(5) << "\n";
break;
case sh::Command::unknown:
result = sh::isExec(input.substr(0, input.find(' ')));
if (result)
{
std::system(input.c_str());
}
else
{
std::cout << input << ": command not found\n";
}
break;
case sh::Command::type:
sh::printType(input.substr(5));
break;
}
}
}

78
src/shellUtils.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "shellUtils.h"
#include <iostream>
#include <string>
#include <map>
#include <cstdlib>
#include <filesystem>
namespace sh
{
Command getCommand(std::string_view input)
{
if (commandMap.contains(input.substr(0, input.find(' '))))
{
return commandMap.at(input.substr(0, input.find(' ')));
}
else
{
return Command::unknown;
}
}
std::optional<std::string> isExec(const std::string& input)
{
const std::string pathEnv {std::getenv("PATH")};
#if _WIN32
constexpr char delimiter {';'};
#else
constexpr char delimiter {':'};
#endif
size_t startCharIndex{};
size_t nextDelimIndex{pathEnv.find(delimiter)};
while (nextDelimIndex != std::string::npos)
{
std::string currentPath {pathEnv.substr(startCharIndex, nextDelimIndex - startCharIndex)};
std::string fullPath {currentPath + "/" + input};
namespace fs = std::filesystem;
if (!fs::exists(fullPath))
{
startCharIndex = nextDelimIndex + 1;
nextDelimIndex = pathEnv.find(delimiter, startCharIndex);
continue;
}
if ((fs::status(fullPath).permissions() & fs::perms::owner_exec) != fs::perms::owner_exec)
{
startCharIndex = nextDelimIndex + 1;
nextDelimIndex = pathEnv.find(delimiter, startCharIndex);
continue;
}
return fullPath;
}
return std::nullopt;
}
void printType(const std::string& input)
{
const std::string command {input.substr(0, input.find(' '))};
if (commandMap.contains(command))
{
std::cout << command;
std::cout << " is a shell builtin\n";
return;
}
if (auto fullPath {isExec(input)})
{
std::cout << command << " is " << fullPath.value() << "\n";
return;
}
std::cout << command;
std::cout << ": not found\n";
}
}