basic implementation
-added exit, echo, type commands -added ability to execute programs in the $PATH variable
This commit is contained in:
parent
e9651bce0d
commit
f817fc9d7f
|
|
@ -3,6 +3,9 @@ project(shell)
|
|||
|
||||
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_include_directories(shell PRIVATE include)
|
||||
|
|
@ -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);
|
||||
}
|
||||
43
src/main.cpp
43
src/main.cpp
|
|
@ -1,7 +1,46 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
|
||||
#include "shellUtils.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "Hello, World!" << std::endl;
|
||||
return 0;
|
||||
std::cout << std::unitbuf;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue