#include "../osbs.inl"
#include "../bproc.h"
#include <sewer/bmem.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
#include <sewer/unicode.h>
#if !defined(__WINDOWS__)
#error This file is for Windows system
#endif
#include <sewer/nowarn.hxx>
#include <Windows.h>
#include <sewer/warn.hxx>
#define STDIN_READ_CHILD 0
#define STDIN_WRITE_PARENT 1
#define STDOUT_READ_PARENT 2
#define STDOUT_WRITE_CHILD 3
#define STDERR_READ_PARENT 4
#define STDERR_WRITE_CHILD 5
struct _process_t
{
HANDLE pipes[6];
PROCESS_INFORMATION info;
};
static Proc *i_create(HANDLE *pipes, PROCESS_INFORMATION *info)
{
Proc *proc = cast(bmem_malloc(sizeof(Proc)), Proc);
_osbs_proc_alloc();
bmem_copy_n(proc->pipes, pipes, 6, HANDLE);
proc->info = *info;
return proc;
}
static void i_close_pipes(HANDLE *pipes)
{
uint32_t i;
cassert_no_null(pipes);
for (i = 0; i < 6; ++i)
{
if (pipes[i] != NULL)
CloseHandle(pipes[i]);
}
}
static bool_t i_pipes(HANDLE *pipes)
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
bmem_zero_n(pipes, 6, HANDLE);
if (!CreatePipe(&pipes[STDIN_READ_CHILD], &pipes[STDIN_WRITE_PARENT], &saAttr, 0))
{
i_close_pipes(pipes);
return FALSE;
}
if (!SetHandleInformation(pipes[STDIN_WRITE_PARENT], HANDLE_FLAG_INHERIT, 0))
{
i_close_pipes(pipes);
return FALSE;
}
if (!CreatePipe(&pipes[STDOUT_READ_PARENT], &pipes[STDOUT_WRITE_CHILD], &saAttr, 0))
{
i_close_pipes(pipes);
return FALSE;
}
if (!SetHandleInformation(pipes[STDOUT_READ_PARENT], HANDLE_FLAG_INHERIT, 0))
{
i_close_pipes(pipes);
return FALSE;
}
if (!CreatePipe(&pipes[STDERR_READ_PARENT], &pipes[STDERR_WRITE_CHILD], &saAttr, 0))
{
i_close_pipes(pipes);
return FALSE;
}
if (!SetHandleInformation(pipes[STDERR_READ_PARENT], HANDLE_FLAG_INHERIT, 0))
{
i_close_pipes(pipes);
return FALSE;
}
return TRUE;
}
static bool_t i_exec(const char_t *command, HANDLE *pipes, PROCESS_INFORMATION *info)
{
WCHAR commandw[1024] = L"cmd /c ";
STARTUPINFO stinfo;
uint32_t size;
cassert_no_null(pipes);
cassert_no_null(info);
ZeroMemory(&stinfo, sizeof(STARTUPINFO));
stinfo.cb = sizeof(STARTUPINFO);
stinfo.hStdInput = pipes[STDIN_READ_CHILD];
stinfo.hStdOutput = pipes[STDOUT_WRITE_CHILD];
stinfo.hStdError = pipes[STDERR_WRITE_CHILD];
stinfo.dwFlags |= STARTF_USESTDHANDLES;
size = unicode_convers(command, cast((commandw + 7), char_t), ekUTF8, ekUTF16, (1024 - 7) * sizeof(WCHAR));
if (size < (1024 - 7) * sizeof(WCHAR))
{
BOOL ok = CreateProcess(NULL, commandw, NULL, NULL, TRUE, 0, NULL, NULL, &stinfo, info);
if (ok == TRUE)
{
CloseHandle(pipes[STDIN_READ_CHILD]);
CloseHandle(pipes[STDOUT_WRITE_CHILD]);
CloseHandle(pipes[STDERR_WRITE_CHILD]);
pipes[STDIN_READ_CHILD] = NULL;
pipes[STDOUT_WRITE_CHILD] = NULL;
pipes[STDERR_WRITE_CHILD] = NULL;
return TRUE;
}
else
{
return FALSE;
}
}
return FALSE;
}
Proc *bproc_exec(const char_t *command, perror_t *error)
{
HANDLE pipes[6];
PROCESS_INFORMATION info;
if (i_pipes(pipes) == FALSE)
{
ptr_assign(error, ekPPIPE);
return NULL;
}
if (i_exec(command, pipes, &info) == FALSE)
{
i_close_pipes(pipes);
ptr_assign(error, ekPEXEC);
return NULL;
}
ptr_assign(error, ekPOK);
return i_create(pipes, &info);
}
void bproc_close(Proc **proc)
{
cassert_no_null(proc);
cassert_no_null(*proc);
cassert_no_null((*proc)->info.hProcess);
cassert_no_null((*proc)->info.hThread);
i_close_pipes((*proc)->pipes);
CloseHandle((*proc)->info.hProcess);
CloseHandle((*proc)->info.hThread);
_osbs_proc_dealloc();
bmem_free(*dcast(proc, byte_t));
*proc = NULL;
}
bool_t bproc_cancel(Proc *proc)
{
cassert_no_null(proc);
if (TerminateProcess(proc->info.hProcess, 0) != 0)
return TRUE;
else
return FALSE;
}
uint32_t bproc_wait(Proc *proc)
{
DWORD exit_code = 0;
cassert_no_null(proc);
WaitForSingleObject(proc->info.hProcess, INFINITE);
if (GetExitCodeProcess(proc->info.hProcess, &exit_code) != 0)
{
cassert(exit_code != STILL_ACTIVE);
return (uint32_t)exit_code;
}
else
{
return UINT32_MAX;
}
}
bool_t bproc_finish(Proc *proc, uint32_t *code)
{
DWORD exit_code;
cassert_no_null(proc);
if (GetExitCodeProcess(proc->info.hProcess, &exit_code) != 0)
{
if (exit_code != STILL_ACTIVE)
{
ptr_assign(code, (uint32_t)exit_code);
return TRUE;
}
else
{
return FALSE;
}
}
else
{
cassert(FALSE);
return TRUE;
}
}
static bool_t i_read_pipe(HANDLE pipe, byte_t *data, const uint32_t size, uint32_t *rsize, perror_t *error)
{
DWORD lrsize;
BOOL ok;
ok = ReadFile(pipe, (LPVOID)data, (DWORD)size, &lrsize, NULL);
if (ok == TRUE)
{
if (lrsize > 0)
{
ptr_assign(rsize, (uint32_t)lrsize);
ptr_assign(error, ekPOK);
return TRUE;
}
else
{
cassert(lrsize == 0);
ptr_assign(rsize, 0);
ptr_assign(error, ekPOK);
return FALSE;
}
}
else
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
ptr_assign(rsize, 0);
ptr_assign(error, ekPOK);
}
else
{
ptr_assign(rsize, 0);
ptr_assign(error, ekPPIPE);
}
return FALSE;
}
}
bool_t bproc_read(Proc *proc, byte_t *data, const uint32_t size, uint32_t *rsize, perror_t *error)
{
return i_read_pipe(proc->pipes[STDOUT_READ_PARENT], data, size, rsize, error);
}
bool_t bproc_eread(Proc *proc, byte_t *data, const uint32_t size, uint32_t *rsize, perror_t *error)
{
return i_read_pipe(proc->pipes[STDERR_READ_PARENT], data, size, rsize, error);
}
bool_t bproc_write(Proc *proc, const byte_t *data, const uint32_t size, uint32_t *wsize, perror_t *error)
{
DWORD lwsize;
BOOL ok;
cassert_no_null(proc);
ok = WriteFile(proc->pipes[STDIN_WRITE_PARENT], (LPCVOID)data, (DWORD)size, &lwsize, NULL);
if (ok == TRUE)
{
ptr_assign(wsize, (uint32_t)lwsize);
ptr_assign(error, ekPOK);
return TRUE;
}
else
{
ptr_assign(error, ekPOK);
return FALSE;
}
}
bool_t bproc_read_close(Proc *proc)
{
cassert_no_null(proc);
if (proc->pipes[STDOUT_READ_PARENT] != NULL)
{
CloseHandle(proc->pipes[STDOUT_READ_PARENT]);
proc->pipes[STDOUT_READ_PARENT] = NULL;
return TRUE;
}
return FALSE;
}
bool_t bproc_eread_close(Proc *proc)
{
cassert_no_null(proc);
if (proc->pipes[STDERR_READ_PARENT] != NULL)
{
CloseHandle(proc->pipes[STDERR_READ_PARENT]);
proc->pipes[STDERR_READ_PARENT] = NULL;
return TRUE;
}
return FALSE;
}
bool_t bproc_write_close(Proc *proc)
{
cassert_no_null(proc);
if (proc->pipes[STDIN_WRITE_PARENT] != NULL)
{
CloseHandle(proc->pipes[STDIN_WRITE_PARENT]);
proc->pipes[STDIN_WRITE_PARENT] = NULL;
return TRUE;
}
return FALSE;
}
void bproc_exit(const uint32_t code)
{
ExitProcess((UINT)code);
}