← index
MasterX_System/system/src/apps/cmd/cmd_shell.cpp
Source: MasterX_System/system/src/apps/cmd/cmd_shell.cpp
#include "cmd_shell.h"
#include "scanner.hpp"
#include "parser.hpp"
#include "ast.hpp"
#include "command.hpp"
#include <sstream>
#include <iostream>
#include <algorithm>

static bool cmd_initialized = false;
static bool last_command_succeeded = true;

void initCmdShell() {
    if (cmd_initialized) return;
    cmd::AstExecutor::getExecutor();
    cmd_initialized = true;
}

void setCmdUpdateCallback(std::function<void(const std::string&)> callback) {
    cmd::AstExecutor &executor = cmd::AstExecutor::getExecutor();
    executor.setUpdateCallback(callback);
#if defined(__EMSCRIPTEN__)
    setWasmRenderCallback(callback);
#endif
}

void setCmdInputCallback(std::function<std::string()> callback) {
    cmd::AstExecutor &executor = cmd::AstExecutor::getExecutor();
    executor.setInputCallback(callback);
}

void setCmdFlushCallback(std::function<void(const std::string&)> callback) {
    cmd::AstExecutor &executor = cmd::AstExecutor::getExecutor();
    executor.setFlushCallback(callback);
}

static int countWord(const std::string& input, const std::string& word) {
    int count = 0;
    size_t pos = 0;
    while ((pos = input.find(word, pos)) != std::string::npos) {
        // Check word boundary before
        bool validStart = (pos == 0 || !std::isalnum(input[pos - 1]));
        // Check word boundary after
        bool validEnd = (pos + word.length() >= input.length() || 
                        !std::isalnum(input[pos + word.length()]));
        if (validStart && validEnd) {
            count++;
        }
        pos += word.length();
    }
    return count;
}

static bool endsWithBackslash(const std::string& input) {
    if (input.empty()) return false;
    // Find last non-whitespace character
    size_t lastNonSpace = input.find_last_not_of(" \t\n\r");
    if (lastNonSpace != std::string::npos && input[lastNonSpace] == '\\') {
        return true;
    }
    return false;
}

MultiLineState checkMultiLineState(const std::string& input) {
    MultiLineState state;
    state.needsMoreInput = false;
    state.blockDepth = 0;
    state.lineContinuation = false;
    
    if (input.empty()) {
        return state;
    }
    
    if (endsWithBackslash(input)) {
        state.lineContinuation = true;
        state.needsMoreInput = true;
        return state;
    }
    int forCount = countWord(input, "for");
    int whileCount = countWord(input, "while");
    int ifCount = countWord(input, "if");
    int defineCount = countWord(input, "define");
    
    int doneCount = countWord(input, "done");
    int fiCount = countWord(input, "fi");
    int endCount = countWord(input, "end");
    int loopDepth = (forCount + whileCount) - doneCount;
    int ifDepth = ifCount - fiCount;
    int defineDepth = defineCount - endCount;
    
    state.blockDepth = loopDepth + ifDepth + defineDepth;
    
    if (state.blockDepth > 0) {
        state.needsMoreInput = true;
    }
    
    return state;
}

std::string getCommandName(const std::string& command) {
    std::string trimmed = command;
    size_t start = trimmed.find_first_not_of(" \t\n\r");
    if (start == std::string::npos) return "";
    trimmed = trimmed.substr(start);
    size_t end = trimmed.find_first_of(" \t\n\r");
    if (end == std::string::npos) {
        return trimmed;
    }
    return trimmed.substr(0, end);
}

bool isCommandRegistered(const std::string& cmdName) {
    cmd::AstExecutor &executor = cmd::AstExecutor::getExecutor();
    return executor.getCommandRegistry().commandExists(cmdName);
}

bool looksLikeStatement(const std::string& command) {
    size_t eq_pos = command.find('=');
    if (eq_pos != std::string::npos) {
        return true;
    }
    
    std::string trimmed = command;
    size_t start = trimmed.find_first_not_of(" \t\n\r");
    if (start != std::string::npos) {
        trimmed = trimmed.substr(start);
        if (trimmed.find("for ") == 0 || trimmed.find("while ") == 0 || 
            trimmed.find("if ") == 0 || trimmed.find("function ") == 0 ||
            trimmed.find("define ") == 0 || trimmed.find("return ") == 0 || 
            trimmed.find("break") == 0 || trimmed.find("continue") == 0) {
            return true;
        }
    }
    
    return false;
}

std::string executeCmd(const std::string& command) {
    if (!cmd_initialized) {
        initCmdShell();
    }    
    if (command.empty()) {
        return "";
    }
    
    std::ostringstream out_stream;
    std::ostringstream err_stream;
    std::streambuf* original_cerr = std::cerr.rdbuf();
    std::cerr.rdbuf(err_stream.rdbuf());
    
    std::string result;
    last_command_succeeded = true;
    
    try {
        std::stringstream input_stream(command);
        scan::TString string_buffer(command);
        scan::Scanner scanner(string_buffer);
        cmd::Parser parser(scanner);
        auto ast = parser.parse();
        cmd::AstExecutor &executor = cmd::AstExecutor::getExecutor();
        executor.execute(input_stream, out_stream, ast);
        std::cerr.flush();
        result = out_stream.str() + err_stream.str();
    } catch(const scan::ScanExcept &e) {
        std::cerr.flush();
        last_command_succeeded = false;
        result = out_stream.str() + err_stream.str() + "Scanner Error: " + e.why() + "\n";
    } catch(const cmd::Exit_Exception &e) {
        std::cerr.flush();
        if (e.getCode() == 0) {
            result = out_stream.str() + err_stream.str();
        } else {
            result = out_stream.str() + err_stream.str() + "Exit with code: " + std::to_string(e.getCode()) + "\n";
        }
    } catch(const std::runtime_error &e) {
        std::cerr.flush();
        last_command_succeeded = false;
        result = out_stream.str() + err_stream.str() + "Runtime Error: " + std::string(e.what()) + "\n";
    } catch(const cmd::AstFailure &e) {
        std::cerr.flush();
        last_command_succeeded = false;
        result = out_stream.str() + err_stream.str() + "Failure: " + std::string(e.what()) + "\n";
    } catch(const std::exception &e) {
        std::cerr.flush();
        last_command_succeeded = false;
        result = out_stream.str() + err_stream.str() + "Error: " + std::string(e.what()) + "\n";
    } catch(...) {
        std::cerr.flush();
        last_command_succeeded = false;
        result = out_stream.str() + err_stream.str() + "Unknown error occurred\n";
    }
    
    std::cerr.rdbuf(original_cerr);
    if (result.empty() && last_command_succeeded && !looksLikeStatement(command)) {
        std::string cmdName = getCommandName(command);
        if (!cmdName.empty() && !isCommandRegistered(cmdName)) {
            return "Command not found: " + cmdName + "\n";
        }
    }
    
    return result;
}