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)
|
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)
|
||||||
|
|
@ -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 <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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