Skip to content
Snippets Groups Projects
Select Git revision
  • 83e2bfb668355333b1be0228478968bdfbfd8a5e
  • main default protected
  • v0.6
  • v0.5.1
  • v0.2.1
5 results

nosh.cpp

Blame
  • Forked from Zsombor Szabó-Antalovszky / Noémi Shell
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    nosh.cpp 17.99 KiB
    /**
     *  Copyright (c) 2025 Zsombor Szabó-Antalovszky
     * 
     * -------------------------------------------------------------------------------------------------------------------
     *  Dear future programmer,
     * 
     *  When I started writing this, I was feeling pretty good about it, thinking I had everything
     *  figured out. But as it grew, I'm starting to think maybe only the universe knows how this
     *  thing really works.
     * 
     *  If, for some crazy reason, someone actually ends up using or expanding on this open-source code,
     *  I feel for you. If things break in ways I couldn't even predict (and they will),
     *  just go ahead and bump this counter up:
     * 
     *  total_hours_wasted_here = 254;
     * 
     *  -------------------------------------------------------------------------------------------------------------------
     *  
     *  Noémi Shell (nosh). An open-source Shell developed for Windows-based systems
     * 
     *  Handles shell operations, heavily inspired by bash and zsh.
     * 
     *  @author Zsombor Szabó-Antalovszky
     *  @author ChatGPT (a lot of help)
     *  
     *  Licensed under a modified MIT License. See the license on the Noémi Shell GitLab repo for details.
     */
    
    
    #include <iostream>
    #include <string>   // Handling strings
    #include <vector>   
    #include <windows.h> // For using the Windows API
    #include <filesystem>  // For file system operations
    #include <fstream>  // Reading/Creating files
    #include <cstring>  // String operations
    #include <regex> // For grasp filtering
    #include <cstdlib>
    
    const char VERSION[] = "0.3.1"; //< Change version number here for the entire code
    #define STRLEN 100 //< Max string length
    
    char vault[STRLEN]; //< Storing custom info;
    
    namespace fs = std::filesystem;
    
    using namespace std;
    
    // Declaring functions
    
    void handle_pwd(const vector<string>& args);
    bool is_builtin_command(const vector<string>& args);
    void handle_noemi(const vector<string>& args);
    void handle_list(const vector<string>& args);
    void handle_cd(const vector<string>& args);
    void handle_storage(const vector<string>& args);
    void handle_read(const vector<string>& args, string filename);
    void handle_grasp(const vector<string>& args);
    void handle_tap(const vector<string>& args);
    void handle_crush(const vector<string>& args);
    void handle_mkdir(const vector<string>& args);
    void handle_rn(const vector<string>& args);
    void handle_snatch(const vector<string>& args);
    void handle_exec(const vector<string>& args);
    
    /**
     *  The main code
     */
    
    int main() {
        
        cout << "Noemi Shell (nosh)\n";
        cout << "Copyright (C) 2025 Zsombor Szabo-Antalovszky\n\n";
    
        string command; // Storing command
    
        while (true) {
            cout << "nosh> ";
            getline(cin, command);
    
            // On 'exit' command
            if (command == "exit"){
                cout << "Exiting...\n";
                break;
            }
    
            vector<string> args;
            string temp;
            bool in_quotes = false;
    
            for (size_t i = 0; i < command.size(); ++i) {
                char ch = command[i];
                
                if (ch == '"') { // Toggle in_quotes state on encountering a quote
                    in_quotes = !in_quotes;
                } 
                else if (ch == ' ' && !in_quotes) { // Treat space as separator only if not in quotes
                    if (!temp.empty()) {
                        args.push_back(temp);
                        temp.clear();
                    }
                } 
                else {
                    temp += ch;
                }
            }
    
            // Push the last argument if any
            if (!temp.empty()) {
                args.push_back(temp);
            }
    
            // Ignore empty commands
            if (args.empty()) {
                continue;
            }
    
            // Example: Handling unrecognized commands
            if (!is_builtin_command(args)) {
                cout << "Unrecognized command: ";
                for (const auto& arg : args) {
                    cout << arg << " ";
                }
                cout << endl;
            }
        }
    
    
        return 0; // Terminate program with success
    }
    
    
    /**
     *  Runs executable files using the 'system' function
     * 
     *  @param args for arguments
     */
    void handle_exec(const vector<string>& args){
        if (args.size() != 2){
            cout << "Usage: exec <file_to_execute>\n";
            return; // Exit early
        }
    
        string path = args[1];
    
        system(path.c_str()); // Runs the executable
    }
    
    
    /**
     *  Handling the 'pwd' command for printing the working directory
     * 
     *  @param args for arguments.
     */
    void handle_pwd(const vector<string>& args){
    
        if (args.size() > 1) {
            cerr << "pwd: too many arguments/n"; // In case any arguments are found
            return; // Exit early
        }
    
        char buffer[MAX_PATH]; // Buffer for path
    
        // Gets the current directory
        if (GetCurrentDirectoryA(MAX_PATH, buffer)) {
            cout << buffer << endl;
        }
    }
    
    /**
     *  Function for checking/handling builtin commands, and executing them accordingly
     * 
     *  @param args for the arguments
     *  @return bool true of false whether the command exists or not
     */
    
    bool is_builtin_command(const vector<string>& args){
    
        if (args[0] == "pwd") {
            handle_pwd(args);
            return true;
        }
    
        if (args[0] == "about") {
            
            if (args.size() > 2){
                cout << "about: too many arguments\n";
                return true;
            }
    
            if (args.size() == 2) {
                if (args[1] == "--version"){
                    cout << "v" << VERSION;
                    cout << "\n";
                    return true;
                }
                else{
                    cout << "unrecognized argument: " << args[1];
                    return true;
                }
            }
        
            cout << "\nNoemi Shell v" << VERSION;
            cout << "\n";
            cout << "Copyright (C) 2025 Zsombor Szabo-Antalovszky \n" << endl;
            return true;
        }
    
        if (args[0] == "clear") {
            if (args.size() > 1) {
                cout << "clear: too many arguments";
                return true;
            }
    
            system("cls");
            return true;
        }
    
        if (args[0] == "noemi"){
              
            handle_noemi(args);
            return true;
        }
    
        if (args[0] == "cd"){
            handle_cd(args);
            return true;
        }
    
        if (args[0] == "exec"){
            handle_exec(args);
            return true;
        }
    
        if (args[0] == "list") {
            handle_list(args);
            return true;
        }
    
        if (args[0] == "store") {
            handle_storage(args);
            return true;
        }
    
        if (args[0] == "read") {
            handle_read(args, args[1]);
            return true;
        }
    
        if (args[0] == "grasp"){
            handle_grasp(args);
            return true;
        }
    
        if (args[0] == "tap"){
            handle_tap(args);
            return true;
        }
    
        if (args[0] == "crush"){
            handle_crush(args);
            return true;
        }
    
        if (args[0] == "mkdir"){
            handle_mkdir(args);
            return true;
        }
    
        if (args[0] == "rn"){
            handle_rn(args);
            return true;
        }
    
        return false; // Return false if given command does not exist
    }
    
    /**
     *  Though the name sounds dirty, this is a very lovely function that draws a heart out of '#' characters.
     *  every time the user enters the command 'noemi'
     * 
     *  @param args for arguments
     *  @param time for adjusting smoothness of draw
     */
    
    void handle_noemi(const vector<string>& args){
    
        if (args.size() > 2){
            cout << "Usage: noemi <delay argument> ";
            return; // Exit early
        }
    
        int time = 60; // Default time 60;
    
        // In case an argument follows the command
        if (args.size() == 2){
            const string option = args[1]; // Set the option variable to the first argument
            
            if (option == "-l") time = 500; // 500 milliseconds between each line
            
            else{
                cout << "noemi: unrecognized argument " << args[1] << "\n";
                return; // Exit early;
            }
        }
    
        cout << "             \n\t      #######       #######\n";
        Sleep(time);
        cout << "            ###########   ###########\n";
        Sleep(time);
        cout << "          ############### ###############\n";
        Sleep(time);
        cout << "         #################################\n";
        Sleep(time);
        cout << "        ###################################\n";
        Sleep(time);
        cout << "        ###################################\n";
        Sleep(time);
        cout << "         #################################\n";
        Sleep(time);
        cout << "          ###############################\n";
        Sleep(time);
        cout << "            ###########################\n";
        Sleep(time);
        cout << "              #######################\n";
        Sleep(time);
        cout << "               ###################\n";
        Sleep(time);
        cout << "                 ###############\n";
        Sleep(time);
        cout << "                    #######\n";
        Sleep(time);
        cout << "                      ###\n";
        Sleep(time);
        cout << "                       #\n\n";
    }
    
    /**
     *  Handling the 'list' command for listing files and directories.
     * 
     *  @param args for arguments.
     */
    void handle_list(const vector<string>& args){
        fs::path target_path = fs::current_path();
    
        if (args.size() > 1) {
            
            target_path = args[1]; // Target path is the first arguments
            
            // Checks if directory exists
            if (!fs::exists(target_path) || !fs::is_directory(target_path)) {
                cerr << "list: " << args[1] << ": No such directory" << endl;
                return; // Exit early
            }
        }
    
        try {
            // If directory, mark with [DIR]. If file, mark with [FILE]
            for (const auto& entry : fs::directory_iterator(target_path)){
                cout << (entry.is_directory() ? "[DIR] " : "[FILE] ");
                cout << entry.path().filename().string() << endl;
            }
        }  catch (const fs::filesystem_error e) {
            // If there is an error reading directory
            cerr << "list: Error reading directory: " << e.what() << endl;
        }
    }
    
    /**
     *  Handling the 'cd' command for changing directories
     * 
     *  @param args for arguments.
     */
    void handle_cd(const vector<string>& args) {
        if (args.size() > 2) {
            cout << "cd: too many arguments\n";
            return; // Exit early
        }
    
        if (args.size() == 1) {
            // No argument, go to home directory
            const char* home_dir = getenv("USERPROFILE");  // Get home directory on Windows
            if (home_dir) {
                fs::path home_path = home_dir;
                fs::current_path(home_path);  // Change to home directory
                cout << "Changed directory to: " << home_path << endl;
            } else {
                cout << "cd: Home directory not found.\n";
            }
        }
        else if (args[1] == "..") {
            // cd .., go up one directory level
            fs::path current_path = fs::current_path();
            fs::path parent_path = current_path.parent_path(); // Get the parent directory
            fs::current_path(parent_path); // Change the current path
            cout << "Changed directory to: " << parent_path << endl;
        }
        else {
            // Try to change to the directory specified by the user
            fs::path new_path = args[1];
            if (fs::exists(new_path) && fs::is_directory(new_path)) {
                fs::current_path(new_path);
                cout << "Changed directory to: " << new_path << endl;
            } else {
                cout << "cd: " << new_path << ": No such directory\n";
            }
        }
    }
    
    /**
     *  Handling the 'read' command for reading text from text based files.
     * 
     *  @param args for arguments.
     */
    void handle_read(const vector<string>& args, string filename) {
        // If there is no argument after command
        if (args.size() != 2) {
            cout << "Usage: read <filename>\n";
            return; // Exit early
        }
    
        // Open the file for reading
        ifstream file(filename);
        if (!file.is_open()) {
            cerr << "read: unable to open file: " << filename << "\n";
            return; // Exit early
        }
    
        // Read and display file contents
        string line;
        bool isEmpty = true;
        while (getline(file, line)) {
            cout << line << "\n";
            isEmpty = false;
        }
    
        if (isEmpty) {
            cout << "read: file is empty: " << filename << "\n";
        }
    
        file.close(); // CLosing the file 
    }
    
    /**
     *  Handling the 'store' command for storing a string of maximum 100 characters during runtime.
     * 
     *  @param args for arguments.
     */
    void handle_storage(const vector<string>& args) {
        if (args.size() == 1) {
            // Store new text in memory
            cout << "Enter text to store in memory (MAX 100 characters): ";
            cin.getline(vault, STRLEN); // Use getline to capture input
            cout << "Storing: \"" << vault << "\" in memory.\n";
        } else if (args.size() == 2 && args[1] == "-g") {
            // Retrieve stored text
            if (strlen(vault) == 0) {
                cout << "No text stored in memory.\n";
            } else {
                cout << "Stored text: \"" << vault << "\"\n";
            }
        } else if (args.size() == 2) {
            // Handle unrecognized arguments
            cout << "store: unrecognized argument: " << args[1] << "\n";
        } else {
            // Handle too many arguments
            cout << "store: too many arguments\n";
        }
    }
    
    /**
     *  Handling the 'grasp' command for filtering results
     * 
     *  @param args for arguments.
     */
    void handle_grasp(const vector<string>& args){
        if (args.size() != 4){
            cout << "Usage: grasp <type> <directory> <pattern>\n";
            return; // Exits early
        }
    
        const string type = args[1];
        const string dirPath = args[2];
        const string pattern = args[3];
    
        // IF the type argument is invalid
        if (args[1] != "-d" && args[1] != "-f"){
            cout << "grasp: invalid argument for type";
            return; // Exit early
        }
    
        if (args[1] == "-d") {
            if (!filesystem::exists(dirPath)) {
                cout << "grasp: the directory " << dirPath << " does not exist.\n";
                return; // Exit early
            }
    
            if (!filesystem::is_directory(dirPath)) {
                cout << "grasp: " << dirPath << " is not a directory.\n";
                return; // Exit early
            }
    
            // Iterate through directory contents
            for (const auto& entry : filesystem::directory_iterator(dirPath)) {
                const string name = entry.path().filename().string(); // Get the name of the entry
    
                // Check if it's a directory and matches the pattern
                if (entry.is_directory() && name.find(pattern) != string::npos) {
                    cout << name << endl; // Print matching directory name
                }
            }
        }
    
        if (type == "-f"){
            
           // Check if the directory exists
            if (!filesystem::exists(dirPath)) {
                cout << "grasp: the directory " << dirPath << " does not exist.\n";
                return; // Exit early
            }
    
            // Ensure it's a directory
            if (!filesystem::is_directory(dirPath)) {
                cout << "grasp: " << dirPath << " is not a directory.\n";
                return; // Exit early
            }
    
            // Iterate through directory contents
            for (const auto& entry : filesystem::directory_iterator(dirPath)) {
                const string name = entry.path().filename().string();
    
                // Check if it's a file and matches the pattern
                if (entry.is_regular_file() && name.find(pattern) != string::npos) {
                    cout << name << endl; // Print matching file name
                }
            }
    
        }    
    }
    
    /**
     *  Handling the 'tap' command for creating files.
     * 
     *  @param args for arguments.
     */
    void handle_tap(const vector<string>& args){
        if (args.size() != 2){
            cout << "Usage: tap <filename>\n";
            return;
        }
    
        const string filename = args[1];
    
        ofstream file(args[1]);
    
        if (!file) {
            cerr << "tap: Unable to create file: " << filename;
            return;
        }
    
        file.close();
    }
    
    /**
     *  Handling the 'crush' command for removing files and directories.
     * 
     *  @param args for arguments.
     */
    void handle_crush(const vector<string>& args) {
        
        if (args.size() != 3){
            cout << "usage: crush <type> <filename>\n";
            return;
        }
    
        const string type = args[1];
        const string filename = args[2];
    
        if (type == "-f"){
            
            if (filesystem::exists(filename)){
                filesystem::remove(filename);
            }
            else{
                cout << "crush: File does not exist: " << filename << endl;
            }
            
            
        }
    
        if (type == "-d") {
            if (std::filesystem::is_directory(filename)) {
                
                char prompt;
                
                cout << "Are you sure you want to remove: " << filename << "?\n";
                cout << "There is NO UNDO! \n";
                cout << "Choose Y/N: ";
                cin >> prompt;
    
                cin.ignore(numeric_limits<streamsize>::max(), '\n');
    
                switch (toupper(prompt)) {
                case 'Y':
                    // Pass the directory path to remove_all
                    std::filesystem::remove_all(filename);  // This removes the directory and all its contents
                    cout << "Directory and its contents have been removed: \n" << filename << endl;
                    break;
                
                default:
                    cout << "Operation cancelled.\n";
                    break;
                }
            } else {
                cout << "The specified path is not a valid directory: " << filename << endl;
            }
        }   
    
    }
    
    /**
     *  Handling the 'mkdir' command for creating directories.
     * 
     *  @param args for arguments.
     */
    void handle_mkdir(const vector<string>& args) {
        if (args.size() != 2){
            cout << "Usage: mkdir <directory_name>\n";
            return;
        }
    
        const string dirName = args[1];
    
        if (fs::exists(dirName)) {
            cout << "Directory already exists: " << dirName << endl;
            return;
        }
    
        // Create the directory
        if (!fs::create_directory(dirName)) {
            cout << "Failed to create directory: " << dirName << endl;
        }
    }
    
    /**
     *  Handling the 'rn' command for renaming files and directories.
     * 
     *  @param args for arguments.
     */
    void handle_rn(const vector<string>& args){
        if (args.size() != 3){
            cout << "Usage: rn <filename> <rename>\n";
            return;
        }
    
        const string filename = args[1];
        const string rename = args[2];
    
        try
        {
            filesystem::rename(filename, rename);
        }
        catch(const std::exception& e)
        {
            cerr << "Error: " << e.what() << '\n';
        }
    }