Added process
This commit is contained in:
@@ -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"
|
||||||
+132
-1
@@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
#include <desktoplib/terminal/Terminal.hpp>
|
#include <desktoplib/terminal/Terminal.hpp>
|
||||||
#include <desktoplib/terminal/ui/Box.hpp>
|
#include <desktoplib/terminal/ui/Box.hpp>
|
||||||
|
#include <desktoplib/proc/Process.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
int main() {
|
/*
|
||||||
|
// Original Terminal test - commented out
|
||||||
|
int main_terminal() {
|
||||||
using namespace ckitty::terminal;
|
using namespace ckitty::terminal;
|
||||||
Terminal term;
|
Terminal term;
|
||||||
|
|
||||||
@@ -111,5 +114,133 @@ int main() {
|
|||||||
|
|
||||||
return 0;
|
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
|
#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
|
||||||
@@ -22,6 +22,10 @@ namespace ckitty {
|
|||||||
|
|
||||||
void writeInput(ProcessInfo* info, std::string_view data);
|
void writeInput(ProcessInfo* info, std::string_view data);
|
||||||
|
|
||||||
|
void closeInput(ProcessInfo* info);
|
||||||
|
|
||||||
|
void terminate(ProcessInfo* info);
|
||||||
|
|
||||||
std::string readOutput(ProcessInfo* info);
|
std::string readOutput(ProcessInfo* info);
|
||||||
|
|
||||||
std::string readError(ProcessInfo* info);
|
std::string readError(ProcessInfo* info);
|
||||||
|
|||||||
@@ -125,6 +125,20 @@ namespace ckitty {
|
|||||||
write(info->stdin_fd, data.data(), data.size());
|
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) {
|
std::string readOutput(ProcessInfo* info) {
|
||||||
if (!info) return "";
|
if (!info) return "";
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ namespace ckitty {
|
|||||||
SetHandleInformation(hStderrRead, HANDLE_FLAG_INHERIT, 0);
|
SetHandleInformation(hStderrRead, HANDLE_FLAG_INHERIT, 0);
|
||||||
SetHandleInformation(hStdinWrite, 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.dwFlags = STARTF_USESTDHANDLES;
|
||||||
si.hStdOutput = hStdoutWrite;
|
si.hStdOutput = hStdoutWrite;
|
||||||
si.hStdError = hStderrWrite;
|
si.hStdError = hStderrWrite;
|
||||||
@@ -130,6 +132,20 @@ namespace ckitty {
|
|||||||
WriteFile(info->hStdin, data.data(), static_cast<DWORD>(data.size()), &written, nullptr);
|
WriteFile(info->hStdin, data.data(), static_cast<DWORD>(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) {
|
std::string readOutput(ProcessInfo* info) {
|
||||||
if (!info) return "";
|
if (!info) return "";
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
namespace ckitty {
|
namespace ckitty {
|
||||||
|
|
||||||
Process::~Process() {
|
Process::~Process() {
|
||||||
if (ProcessInfo) {
|
if (info) {
|
||||||
ckitty::proc::os::destroyProcess(ProcessInfo);
|
ckitty::proc::os::destroyProcess(info);
|
||||||
ProcessInfo = nullptr;
|
info = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,29 +20,40 @@ namespace ckitty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Process::run() {
|
void Process::run() {
|
||||||
if (ProcessInfo) return;
|
if (info) return;
|
||||||
ProcessInfo = ckitty::proc::os::createProcess(exec_, args_);
|
info = ckitty::proc::os::createProcess(exec_, args_);
|
||||||
|
failed_ = (info == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Process::isRunning() {
|
bool Process::isRunning() {
|
||||||
if (!ProcessInfo) return false;
|
if (!info) return false;
|
||||||
return ckitty::proc::os::isRunning(ProcessInfo);
|
return ckitty::proc::os::isRunning(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Process::stopCode() {
|
int Process::stopCode() {
|
||||||
if (!ProcessInfo) return 0;
|
if (!info) return 0;
|
||||||
return ckitty::proc::os::getExitCode(ProcessInfo);
|
return ckitty::proc::os::getExitCode(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::addInput(std::string_view view) {
|
void Process::addInput(std::string_view view) {
|
||||||
if (!ProcessInfo) return;
|
if (!info) return;
|
||||||
ckitty::proc::os::writeInput(ProcessInfo, view);
|
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() {
|
std::string_view Process::getOutput() {
|
||||||
if (!ProcessInfo) return "";
|
if (!info) return "";
|
||||||
static std::string accumulated;
|
static std::string accumulated;
|
||||||
std::string fresh = ckitty::proc::os::readOutput(ProcessInfo);
|
std::string fresh = ckitty::proc::os::readOutput(info);
|
||||||
if (!fresh.empty()) {
|
if (!fresh.empty()) {
|
||||||
accumulated += fresh;
|
accumulated += fresh;
|
||||||
}
|
}
|
||||||
@@ -50,10 +61,14 @@ namespace ckitty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string_view Process::nextOutput() {
|
std::string_view Process::nextOutput() {
|
||||||
if (!ProcessInfo) return "";
|
if (!info) return "";
|
||||||
static std::string fresh;
|
static std::string fresh;
|
||||||
fresh = ckitty::proc::os::readOutput(ProcessInfo);
|
fresh = ckitty::proc::os::readOutput(info);
|
||||||
return fresh;
|
return fresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Process::failed() const {
|
||||||
|
return failed_;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -10,9 +10,10 @@ namespace ckitty {
|
|||||||
|
|
||||||
class Process {
|
class Process {
|
||||||
private:
|
private:
|
||||||
ckitty::proc::os::ProcessInfo* ProcessInfo = nullptr;
|
ckitty::proc::os::ProcessInfo* info = nullptr;
|
||||||
std::string exec_;
|
std::string exec_;
|
||||||
std::vector<std::string> args_;
|
std::vector<std::string> args_;
|
||||||
|
bool failed_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Process() = default;
|
Process() = default;
|
||||||
@@ -24,8 +25,11 @@ namespace ckitty {
|
|||||||
|
|
||||||
bool isRunning();
|
bool isRunning();
|
||||||
int stopCode();
|
int stopCode();
|
||||||
|
bool failed() const;
|
||||||
|
|
||||||
void addInput(std::string_view view);
|
void addInput(std::string_view view);
|
||||||
|
void closeInput();
|
||||||
|
void terminate();
|
||||||
std::string_view getOutput();
|
std::string_view getOutput();
|
||||||
std::string_view nextOutput();
|
std::string_view nextOutput();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user