diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..4cd8284 --- /dev/null +++ b/run.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +BOLD="\033[1m" +RESET="\033[0m" + +SUCCESS="\033[38;2;80;220;100m" +FAIL="\033[38;2;255;90;90m" +INFO="\033[38;2;100;200;255m" +DIM="\033[2m" + +LOG="./out/out.log" + +# Start +printf "${DIM}────────────────────────────────────────${RESET}\n" +printf "${INFO}${BOLD}> Running...${RESET}\n" +printf "${DIM}────────────────────────────────────────${RESET}\n\n" + +# Send both stdout and stderr to the console and the log file. +start=$(date +%s.%N) +./out/out "$@" 2>&1 | tee "$LOG" +status=${PIPESTATUS[0]} +end=$(date +%s.%N) +elapsed=$(awk "BEGIN { printf \"%.3f\", $end - $start }") + +# Status! +printf "\n${DIM}────────────────────────────────────────${RESET}\n" +if (( status == 0 )); then + printf "${SUCCESS}${BOLD}✓ Success${RESET} (exit %d)\n" "$status" +else + printf "${FAIL}${BOLD}✗ Failed${RESET} (exit %d)\n" "$status" +fi +printf "${INFO}${BOLD}> Time${RESET} ${elapsed}s\n" +printf "${INFO}${BOLD}> Log${RESET} %s\n" "$LOG" +printf "${DIM}────────────────────────────────────────${RESET}" +exit "$status" diff --git a/src/desktoplib/main.cpp b/src/desktoplib/main.cpp index eaf3505..9b2b576 100644 --- a/src/desktoplib/main.cpp +++ b/src/desktoplib/main.cpp @@ -2,12 +2,15 @@ #include #include +#include #include #include #include #include -int main() { +/* +// Original Terminal test - commented out +int main_terminal() { using namespace ckitty::terminal; Terminal term; @@ -111,5 +114,133 @@ int main() { return 0; } +*/ +// Process test +int main() { + using namespace ckitty; + + std::cout << "=== Process Test ===\n"; + + // Test 1: Simple command execution + { + std::cout << "\n--- Test 1: echo command ---\n"; + Process proc; +#ifdef _WIN32 + proc.setExec("cmd.exe"); + proc.addParam("/c echo Hello from Process!"); +#else + proc.setExec("echo"); + proc.addParam("Hello from Process!"); #endif + proc.run(); + + while (proc.isRunning()) { + std::string_view out = proc.nextOutput(); + if (!out.empty()) { + std::cout << "Output: " << out; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "Final output: " << proc.getOutput() << "\n"; + std::cout << "Exit code: " << proc.stopCode() << "\n"; + } + +// Test 2: Command with input + { + std::cout << "\n--- Test 2: cat with input ---\n"; + Process proc; +#ifdef _WIN32 + proc.setExec("cmd.exe"); + proc.addParam("/c findstr /r \"^\""); +#else + proc.setExec("cat"); +#endif + proc.run(); + + std::string testInput = "Line 1\nLine 2\nLine 3\n"; + proc.addInput(testInput); + proc.closeInput(); // Close stdin so cat gets EOF + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + while (proc.isRunning()) { + std::string_view out = proc.nextOutput(); + if (!out.empty()) { + std::cout << "Output: " << out; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "Final output: " << proc.getOutput() << "\n"; + std::cout << "Exit code: " << proc.stopCode() << "\n"; + } + + // Test 3: Non-existent command (should fail) + { + std::cout << "\n--- Test 3: Non-existent command ---\n"; + Process proc; + proc.setExec("this_command_does_not_exist_12345"); + proc.run(); + + while (proc.isRunning()) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "Exit code: " << proc.stopCode() << "\n"; + } + + // Test 4: Long running process with timeout + { + std::cout << "\n--- Test 4: Sleep command ---\n"; + Process proc; +#ifdef _WIN32 + proc.setExec("cmd.exe"); + proc.addParam("/c timeout /t 5"); +#else + proc.setExec("sleep"); + proc.addParam("5"); +#endif + proc.run(); + + int polls = 0; + while (proc.isRunning() && polls < 10) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + polls++; + } + + if (proc.isRunning()) { + std::cout << "Process still running, terminating...\n"; + proc.terminate(); + } + + while (proc.isRunning()) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "failed(): " << (proc.failed() ? "true" : "false") << " (expected false)\n"; + std::cout << "Exit code: " << proc.stopCode() << "\n"; + } + + // Test 5: Non-existent command + { + std::cout << "\n--- Test 5: Non-existent command ---\n"; + Process proc; + proc.setExec("this_command_does_not_exist_12345"); + proc.run(); + + std::cout << "failed(): " << (proc.failed() ? "true" : "false") << " (expected true)\n"; + + while (proc.isRunning()) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::cout << "Exit code: " << proc.stopCode() << " (expected 127 on Unix)\n"; + } + + std::cout << "\n=== All Process Tests Complete ===\n"; + return 0; +} + +#endif \ No newline at end of file diff --git a/src/desktoplib/os/common_proc.hpp b/src/desktoplib/os/common_proc.hpp index 7353848..9c3bce2 100644 --- a/src/desktoplib/os/common_proc.hpp +++ b/src/desktoplib/os/common_proc.hpp @@ -22,6 +22,10 @@ namespace ckitty { void writeInput(ProcessInfo* info, std::string_view data); + void closeInput(ProcessInfo* info); + + void terminate(ProcessInfo* info); + std::string readOutput(ProcessInfo* info); std::string readError(ProcessInfo* info); diff --git a/src/desktoplib/os/unix_proc.cpp b/src/desktoplib/os/unix_proc.cpp index 029a6c9..6b54e9b 100644 --- a/src/desktoplib/os/unix_proc.cpp +++ b/src/desktoplib/os/unix_proc.cpp @@ -125,6 +125,20 @@ namespace ckitty { write(info->stdin_fd, data.data(), data.size()); } + void closeInput(ProcessInfo* info) { + if (!info) return; + if (info->stdin_fd != -1) { + close(info->stdin_fd); + info->stdin_fd = -1; + } + } + + void terminate(ProcessInfo* info) { + if (!info || !info->running) return; + kill(info->pid, SIGKILL); + info->running = false; + } + std::string readOutput(ProcessInfo* info) { if (!info) return ""; char buf[4096]; diff --git a/src/desktoplib/os/windows_proc.cpp b/src/desktoplib/os/windows_proc.cpp index b59a1c6..a44c096 100644 --- a/src/desktoplib/os/windows_proc.cpp +++ b/src/desktoplib/os/windows_proc.cpp @@ -45,7 +45,9 @@ namespace ckitty { SetHandleInformation(hStderrRead, HANDLE_FLAG_INHERIT, 0); SetHandleInformation(hStdinWrite, HANDLE_FLAG_INHERIT, 0); - STARTUPINFOA si = { sizeof(STARTUPINFOA) }; + STARTUPINFOA si; + ZeroMemory(&si, sizeof(STARTUPINFOA)); + si.cb = sizeof(STARTUPINFOA); si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = hStdoutWrite; si.hStdError = hStderrWrite; @@ -130,6 +132,20 @@ namespace ckitty { WriteFile(info->hStdin, data.data(), static_cast(data.size()), &written, nullptr); } + void closeInput(ProcessInfo* info) { + if (!info) return; + if (info->hStdin) { + CloseHandle(info->hStdin); + info->hStdin = nullptr; + } + } + + void terminate(ProcessInfo* info) { + if (!info || !info->running) return; + TerminateProcess(info->pi.hProcess, 1); + info->running = false; + } + std::string readOutput(ProcessInfo* info) { if (!info) return ""; char buf[4096]; diff --git a/src/desktoplib/proc/Process.cpp b/src/desktoplib/proc/Process.cpp index eb7524c..399c2ad 100644 --- a/src/desktoplib/proc/Process.cpp +++ b/src/desktoplib/proc/Process.cpp @@ -5,9 +5,9 @@ namespace ckitty { Process::~Process() { - if (ProcessInfo) { - ckitty::proc::os::destroyProcess(ProcessInfo); - ProcessInfo = nullptr; + if (info) { + ckitty::proc::os::destroyProcess(info); + info = nullptr; } } @@ -20,29 +20,40 @@ namespace ckitty { } void Process::run() { - if (ProcessInfo) return; - ProcessInfo = ckitty::proc::os::createProcess(exec_, args_); + if (info) return; + info = ckitty::proc::os::createProcess(exec_, args_); + failed_ = (info == nullptr); } bool Process::isRunning() { - if (!ProcessInfo) return false; - return ckitty::proc::os::isRunning(ProcessInfo); + if (!info) return false; + return ckitty::proc::os::isRunning(info); } int Process::stopCode() { - if (!ProcessInfo) return 0; - return ckitty::proc::os::getExitCode(ProcessInfo); + if (!info) return 0; + return ckitty::proc::os::getExitCode(info); } void Process::addInput(std::string_view view) { - if (!ProcessInfo) return; - ckitty::proc::os::writeInput(ProcessInfo, view); + if (!info) return; + ckitty::proc::os::writeInput(info, view); + } + + void Process::closeInput() { + if (!info) return; + ckitty::proc::os::closeInput(info); + } + + void Process::terminate() { + if (!info) return; + ckitty::proc::os::terminate(info); } std::string_view Process::getOutput() { - if (!ProcessInfo) return ""; + if (!info) return ""; static std::string accumulated; - std::string fresh = ckitty::proc::os::readOutput(ProcessInfo); + std::string fresh = ckitty::proc::os::readOutput(info); if (!fresh.empty()) { accumulated += fresh; } @@ -50,10 +61,14 @@ namespace ckitty { } std::string_view Process::nextOutput() { - if (!ProcessInfo) return ""; + if (!info) return ""; static std::string fresh; - fresh = ckitty::proc::os::readOutput(ProcessInfo); + fresh = ckitty::proc::os::readOutput(info); return fresh; } + bool Process::failed() const { + return failed_; + } + } \ No newline at end of file diff --git a/src/desktoplib/proc/Process.hpp b/src/desktoplib/proc/Process.hpp index 884b9a8..8e8fa89 100644 --- a/src/desktoplib/proc/Process.hpp +++ b/src/desktoplib/proc/Process.hpp @@ -10,9 +10,10 @@ namespace ckitty { class Process { private: - ckitty::proc::os::ProcessInfo* ProcessInfo = nullptr; + ckitty::proc::os::ProcessInfo* info = nullptr; std::string exec_; std::vector args_; + bool failed_ = false; public: Process() = default; @@ -24,8 +25,11 @@ namespace ckitty { bool isRunning(); int stopCode(); + bool failed() const; void addInput(std::string_view view); + void closeInput(); + void terminate(); std::string_view getOutput(); std::string_view nextOutput(); };