I'm learning the file system library and thought nothing could be better than a project, so I thought of recreating the basic Linux commands such as ls, cd etc using C++ and <filesystem>.
As I said, I'm learning C++ and exploring libraries, so there could be weird code and all those things; could you review my code and say where I can improve the code?
My Code:
main.cpp
#include <iostream>
#include <filesystem>
#include "command.h"
namespace fs = std::filesystem;
int main()
{
//Basic Linux Feel ;)
std::string CurPath = fs::current_path();
std::cout << "~";
color::color_blue(CurPath);
std::cout << "$ ";
//Main Loop
while (true)
{
std::string cmnd;
getline(std::cin, cmnd);
#if DEBUG
if (cmnd == "exit")
return 0;
#endif
DetermineCommand(cmnd);
}
}
command.h
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <chrono>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
using namespace std::chrono_literals;
//DEBUG Mode :) I Used This For Exitting The Infinite Loop
#define DEBUG 1
//Used For Setting Color To Console According To OS
class color
{
public:
static void color_green(std::string path)
{
#ifndef __LINUX
//color code for green
std::cout << "\033[32m" << path << "\033[0m";
#else
//Green
system("Color 02");
std::cout << path;
//White
system("Color 07");
#endif
}
static void color_blue(std::string path)
{
//color code for blue
#ifndef __LINUX
std::cout << "\033[34m" << path << "\033[0m";
#else
//Blue
system("Color 01");
std::cout << path << " ";
//White
system("Color 07");
#endif
}
};
//Says The Current Path Color Encoded
void SayPath()
{
std::cout << std::endl;
std::string CurPath = fs::current_path();
std::cout << "~";
color::color_blue(CurPath);
std::cout << "$ ";
}
//cp Used For Copying Files
void cp(std::string p1, std::string p2)
{
fs::path ToCopy = p1, Target = p2;
if (!fs::exists(p2)|| !fs::exists(p1))
{
throw std::string("cp: cannot stat " + p1 + ": No such file or directory");
}
//Trying To Warn
fs::path CheckExist = p2 + "/" + p1;
if (exists(CheckExist))
{
std::cout << "The File Already Exist On The Given Path Replace Them [Y/N] ";
char ans;
std::cin >> ans;
if (ans != 'Y' || ans != 'y')
{
std::cout << "Aborted!";
return;
}
}
fs::copy(p1, p2, fs::copy_options::recursive);
}
//mkdir Used For Making Directories
void mkdir(std::string &s)
{
fs::create_directories(s);
}
//rm Used For Deleteing Files
void rm(std::string s)
{
std::error_code err;
//Handling The Case If Directory Doesn't Exsist
if (!fs::exists(s))
{
throw std::string("rm: cannot remove '" + s + "': No such file or directory");
}
if (fs::is_directory(s, err))
{
if (fs::exists(s))
{
throw std::string("rm: cannot remove '" + s + "': Is a directory");
}
}
fs::remove(s);
return;
}
//The “cd” Command Used For Navigating Over Directories
void cd(std::string &path)
{
//cd .. implementation - going to parent directory
if (path == "..")
{
fs::path Parent = fs::current_path().parent_path();
fs::current_path(Parent);
return;
}
//If The User Entered A Blank Path Or Just The "cd" command
else if (path == "")
{
fs::current_path(fs::current_path().root_directory());
return;
}
//If Path Doesn't Exist
else if (!fs::exists(path))
{
throw std::string("cd: " + path + " No such file or directory");
}
else
{
fs::current_path(path);
}
}
//The “ls” Command Is Used For Printing All The Files And Directories In The Cirrent Folder
void ls()
{
fs::path CurPath = fs::current_path();
//f_name will be used for storing paths and names
std::string f_name;
for (auto &Name : fs::directory_iterator(CurPath))
{
//If It's A File
std::error_code err;
if (fs::is_regular_file((Name).path(), err))
{
f_name = Name.path().filename();
color::color_green(f_name);
std::cout << " ";
}
//If It's A Folder
else
{
f_name = Name.path().filename();
color::color_blue(f_name);
std::cout << " ";
}
}
}
//mv move command
void mv(std::string From, std::string To, bool IsRename)
{
fs::path p1 = From, p2 = To;
//Checking If It's For Renaming
if (!IsRename)
{
fs::path CheckExist = To + "/" + From;
if (exists(CheckExist))
{
std::cout << "The File Already Exist On The Given Path Replace Them [Y/N] ";
char ans;
std::cin >> ans;
if (ans != 'Y' || ans != 'y')
{
std::cout << "Aborted!";
return;
}
}
}
fs::copy(From, To);
fs::remove(From);
}
void rmdir(std::string Path){
if(!fs::exists(Path)){
throw std::string("rmdir: failed to remove "+Path+": No such file or directory");
}
if(!fs::is_directory(Path)){
throw std::string("rmdir: failed to remove "+Path+": Not a directory");
}
fs::remove(Path);
}
//Related To File Information
std::streampos GetSize(std::string PathName){
std::streampos FullSize=0;
fs::path Path=PathName;
if(fs::is_directory(Path)){
for (auto& Name:fs::recursive_directory_iterator(Path)){
if(fs::is_regular_file(Name.path())){
std::ifstream Open(Name.path(),std::ios_base::binary);
std::streampos ThisSize=Open.tellg();
Open.seekg(0,std::ios::end);
FullSize+=ThisSize-Open.tellg();
Open.close();
}
}
return FullSize;
}
else{
std::ifstream Open(Path);
std::streampos ThisSize=Open.tellg();
Open.seekg(0,std::ios::end);
ThisSize=ThisSize-Open.tellg();
Open.close();
return ThisSize;
}
}
void PrintTime(std::string Path){
//Copied From Runebook.dev ;)
auto ftime=fs::last_write_time(Path);
std::time_t cftime = decltype(ftime)::clock::to_time_t(ftime);
std::cout <<std::asctime(std::localtime(&cftime));
}
void GetInfo(std::string Path){
if(!fs::exists(Path)){
throw std::string("--info: "+Path+" No such file or directory");
}
std::streampos Size=GetSize(Path);
std::cout<<"SIZE : "<<Size<<std::endl;
std::cout<<"PATH : "<<Path<<std::endl;
std::cout<<"LAST MODIFIED : ";
PrintTime(Path);
if(fs::is_directory(Path)){
std::cout<<"TYPE : Directory";
}
else{
std::cout<<"TYPE : File";
}
}
void cat(std::string Path){
if(!fs::exists(Path)){
throw std::string("cat: "+Path+": No such file or directory");
}
else if(fs::is_directory(Path)){
throw std::string("cat: "+Path+": Is a directory");
}
std::string Temp;
std::ifstream Reader(Path);
while(getline(Reader,Temp)){
std::cout<<Temp<<std::endl;
}
}
void help(){
std::cout<<"•cd\n•ls\n•mkdir\n•rmdir\n•rm\n•pwd\n•echo\n•clear\n•cp\n•mv\n•rename\n•--info\n•cat";
SayPath();
}
//This Function Is Used For Determining The Type Of Command
void DetermineCommand(std::string &s)
{
std::stringstream Extractor(s);
std::string Type;
Extractor >> Type;
if (Type == "cd")
{
if (s.length() == 2 || s[3] == ' ')
{
std::string Empty = "";
cd(Empty);
SayPath();
return;
}
//I'm Reusing The Type Variable To Store The Path As The Next Would Be The Path
Extractor >> Type;
try
{
cd(Type);
SayPath();
}
catch (std::string Error)
{
std::cout << Error;
SayPath();
return;
}
}
else if (Type == "ls")
{
ls();
SayPath();
}
else if (Type == "mkdir")
{
std::string Path;
Extractor >> Path;
mkdir(Path);
SayPath();
}
else if (Type == "rm")
{
std::string Parms;
Extractor >> Parms;
try
{
rm(Parms);
std::cout << "\b";
}
catch (std::string Error)
{
std::cout << Error;
}
SayPath();
}
else if (Type == "pwd")
{
std::cout << fs::current_path();
SayPath();
}
else if (Type == "echo")
{
std::string s;
std::string a;
Extractor>>s;
while(Extractor>>a){
s+=a+' ';
}
if(s[0]=='"' && s[s.length()-1]=='"'){
s.erase(s.begin());
s.erase(s.end());
}
std::cout << s;
SayPath();
}
else if (Type == "clear")
{
system("clear");
SayPath();
}
else if (Type == "cp")
{
std::string p1, p2;
Extractor >> p1 >> p2;
try
{
cp(p1, p2);
}
catch (std::string Err)
{
std::cout << Err;
}
SayPath();
}
else if (Type == "mv")
{
std::string p1, p2;
Extractor >> p1 >> p2;
try{
mv(p1, p2, false);
}
catch(...){
std::cout<<"Some Error Occured\n";
}
std::cout<<'\b';
SayPath();
}
else if (Type == "rename")
{
std::string p1, p2;
Extractor >> p1 >> p2;
mv(p1, p2, true);
std::cout<<'\b';
SayPath();
}
else if(Type=="--info"){
try{
std::string Path;
Extractor>>Path;
GetInfo(Path);
//Flushing \n
std::cout<<'\b';
SayPath();
}
catch(std::string err){
std::cout<<err;
SayPath();
}
}
else if(Type == "cat"){
std::string FName;
Extractor>>FName;
try{
cat(FName);
}
catch(std::string err){
std::cout<<err<<std::endl;
}
std::cout<<'\b';
SayPath();
}
else if(Type=="--help"){
help();
}
else if(Type == "rmdir"){
std::string Path;
Extractor>>Path;
rmdir(Path);
SayPath();
}
else{
std::cout<<"No such command found use --help to get a list of all command";
SayPath();
}
}
I have uploaded it on GitHub Too - Repo.
Note - I didn't implement all options of every command; for example, I just implemented ls but not ls -a etc.
Thanks for checking this out!!