Browse Source

Now uses files from eidheim/tiny-process-library

merge-requests/365/head
eidheim 10 years ago
parent
commit
e5b6ccb1e7
  1. 1
      README.md
  2. 11
      src/CMakeLists.txt
  3. 25
      src/process.cpp
  4. 81
      src/process.hpp
  5. 183
      src/process_unix.cpp
  6. 281
      src/process_win.cpp

1
README.md

@ -39,6 +39,7 @@ See [enhancements](https://github.com/cppit/jucipp/labels/enhancement) for plann
* aspell
* libclang
* [libclangmm](http://github.com/cppit/libclangmm/)
* [tiny-process-library](http://github.com/eidheim/tiny-process-library/)
## Installation ##
See [installation guide](http://github.com/cppit/jucipp/blob/master/docs/install.md).

11
src/CMakeLists.txt

@ -74,8 +74,6 @@ set(source_files juci.h
cmake.h
cmake.cc
dialogs.cc
process.hpp
process.cpp
../libclangmm/src/CodeCompleteResults.cc
../libclangmm/src/CompilationDatabase.cc
@ -90,14 +88,16 @@ set(source_files juci.h
../libclangmm/src/Tokens.cc
../libclangmm/src/TranslationUnit.cc
../libclangmm/src/Diagnostic.cc
../libclangmm/src/Utility.cc)
../libclangmm/src/Utility.cc
../tiny-process-library/process.cpp)
if(MSYS)
list(APPEND source_files process_win.cpp)
list(APPEND source_files dialogs_unix.cc) #dialogs_win.cc does not work any more because of missing SHCreateItemFromParsingName
list(APPEND source_files ../tiny-process-library/process_win.cpp)
else()
list(APPEND source_files process_unix.cpp)
list(APPEND source_files dialogs_unix.cc)
list(APPEND source_files ../tiny-process-library/process_unix.cpp)
endif()
add_executable(${project_name} ${source_files})
@ -114,6 +114,7 @@ include_directories(
${LIBCLANG_INCLUDE_DIRS}
${ASPELL_INCLUDE_DIR}
../libclangmm/src
../tiny-process-library
)
link_directories(

25
src/process.cpp

@ -1,25 +0,0 @@
#include "process.hpp"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
Process::Process(const std::string &command, const std::string &path,
std::function<void(const char* bytes, size_t n)> read_stdout,
std::function<void(const char* bytes, size_t n)> read_stderr,
bool open_stdin, size_t buffer_size):
closed(true), read_stdout(read_stdout), read_stderr(read_stderr), open_stdin(open_stdin), buffer_size(buffer_size) {
open(command, path);
async_read();
}
Process::~Process() {
close_fds();
}
Process::id_type Process::get_id() {
return data.id;
}
bool Process::write(const std::string &data) {
return write(data.c_str(), data.size());
}

81
src/process.hpp

@ -1,81 +0,0 @@
#ifndef TINY_PROCESS_LIBRARY_HPP_
#define TINY_PROCESS_LIBRARY_HPP_
#include <string>
#include <functional>
#include <vector>
#include <mutex>
#include <thread>
#include <mutex>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/wait.h>
#endif
///Create a new process given command and run path.
///TODO: on Windows it is harder to specify which pipes to redirect.
///Thus, at the moment, if read_stdout==nullptr, read_stderr==nullptr and open_stdin==false,
///the stdout, stderr and stdin are sent to the parent process instead.
///Compile with -DMSYS_PROCESS_USE_SH to run command using "sh -c [command]" on Windows as well.
class Process {
public:
#ifdef _WIN32
typedef DWORD id_type; //Process id type
typedef HANDLE fd_type; //File descriptor type
#else
typedef pid_t id_type;
typedef int fd_type;
#endif
private:
class Data {
public:
Data();
id_type id;
#ifdef _WIN32
HANDLE handle;
#endif
};
public:
Process(const std::string &command, const std::string &path=std::string(),
std::function<void(const char *bytes, size_t n)> read_stdout=nullptr,
std::function<void(const char *bytes, size_t n)> read_stderr=nullptr,
bool open_stdin=false,
size_t buffer_size=131072);
~Process();
///Get the process id of the started process.
id_type get_id();
///Wait until process is finished, and return exit status.
int get_exit_status();
///Write to stdin.
bool write(const char *bytes, size_t n);
///Write to stdin. Convenience function using write(const char *, size_t).
bool write(const std::string &data);
///Close stdin. If the process takes parameters from stdin, use this to notify that all parameters have been sent.
void close_stdin();
///Kill the process.
void kill(bool force=false);
///Kill a given process id. Use kill(bool force) instead if possible.
static void kill(id_type id, bool force=false);
private:
Data data;
bool closed;
std::mutex close_mutex;
std::function<void(const char* bytes, size_t n)> read_stdout;
std::function<void(const char* bytes, size_t n)> read_stderr;
std::thread stdout_thread, stderr_thread;
bool open_stdin;
std::mutex stdin_mutex;
const size_t buffer_size;
std::unique_ptr<fd_type> stdout_fd, stderr_fd, stdin_fd;
id_type open(const std::string &command, const std::string &path);
void async_read();
void close_fds();
};
#endif // TINY_PROCESS_LIBRARY_HPP_

183
src/process_unix.cpp

@ -1,183 +0,0 @@
#include "process.hpp"
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
Process::Data::Data(): id(-1) {}
Process::id_type Process::open(const std::string &command, const std::string &path) {
if(open_stdin)
stdin_fd=std::unique_ptr<fd_type>(new fd_type);
if(read_stdout)
stdout_fd=std::unique_ptr<fd_type>(new fd_type);
if(read_stderr)
stderr_fd=std::unique_ptr<fd_type>(new fd_type);
int stdin_p[2], stdout_p[2], stderr_p[2];
if(stdin_fd && pipe(stdin_p)!=0) {
close(stdin_p[0]);
close(stdin_p[1]);
return -1;
}
if(stdout_fd && pipe(stdout_p)!=0) {
if(stdin_fd) close(stdin_p[0]);
if(stdin_fd) close(stdin_p[1]);
close(stdout_p[0]);
close(stdout_p[1]);
return -1;
}
if(stderr_fd && pipe(stderr_p)!=0) {
if(stdin_fd) close(stdin_p[0]);
if(stdin_fd) close(stdin_p[1]);
if(stdout_fd) close(stdout_p[0]);
if(stdout_fd) close(stdout_p[1]);
close(stderr_p[0]);
close(stderr_p[1]);
return -1;
}
id_type pid = fork();
if (pid < 0) {
if(stdin_fd) close(stdin_p[0]);
if(stdin_fd) close(stdin_p[1]);
if(stdout_fd) close(stdout_p[0]);
if(stdout_fd) close(stdout_p[1]);
if(stderr_fd) close(stderr_p[0]);
if(stderr_fd) close(stderr_p[1]);
return pid;
}
else if (pid == 0) {
if(stdin_fd) close(stdin_p[1]);
if(stdout_fd) close(stdout_p[0]);
if(stderr_fd) close(stderr_p[0]);
if(stdin_fd) dup2(stdin_p[0], 0);
if(stdout_fd) dup2(stdout_p[1], 1);
if(stderr_fd) dup2(stderr_p[1], 2);
setpgid(0, 0);
//TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
//TODO: One solution is: echo "command;exit"|script -q /dev/null
if(!path.empty())
execl("/bin/sh", "sh", "-c", ("cd \""+path+"\" && "+command).c_str(), NULL);
else
execl("/bin/sh", "sh", "-c", command.c_str(), NULL);
_exit(EXIT_FAILURE);
}
if(stdin_fd) close(stdin_p[0]);
if(stdout_fd) close(stdout_p[1]);
if(stderr_fd) close(stderr_p[1]);
if(stdin_fd) *stdin_fd = stdin_p[1];
if(stdout_fd) *stdout_fd = stdout_p[0];
if(stderr_fd) *stderr_fd = stderr_p[0];
closed=false;
data.id=pid;
return pid;
}
void Process::async_read() {
if(data.id<=0)
return;
if(stdout_fd) {
stdout_thread=std::thread([this](){
char buffer[buffer_size];
ssize_t n;
while ((n=read(*stdout_fd, buffer, buffer_size)) > 0)
read_stdout(buffer, static_cast<size_t>(n));
});
}
if(stderr_fd) {
stderr_thread=std::thread([this](){
char buffer[buffer_size];
ssize_t n;
while ((n=read(*stderr_fd, buffer, buffer_size)) > 0)
read_stderr(buffer, static_cast<size_t>(n));
});
}
}
int Process::get_exit_status() {
if(data.id<=0)
return -1;
int exit_status;
waitpid(data.id, &exit_status, 0);
close_mutex.lock();
closed=true;
close_mutex.unlock();
close_fds();
return exit_status;
}
void Process::close_fds() {
if(stdout_thread.joinable())
stdout_thread.join();
if(stderr_thread.joinable())
stderr_thread.join();
if(stdin_fd)
close_stdin();
if(stdout_fd) {
close(*stdout_fd);
stdout_fd.reset();
}
if(stderr_fd) {
close(*stderr_fd);
stderr_fd.reset();
}
}
bool Process::write(const char *bytes, size_t n) {
stdin_mutex.lock();
if(stdin_fd) {
if(::write(*stdin_fd, bytes, n)>=0) {
stdin_mutex.unlock();
return true;
}
else {
stdin_mutex.unlock();
return false;
}
}
stdin_mutex.unlock();
return false;
}
void Process::close_stdin() {
stdin_mutex.lock();
if(stdin_fd) {
close(*stdin_fd);
stdin_fd.reset();
}
stdin_mutex.unlock();
}
void Process::kill(bool force) {
close_mutex.lock();
if(data.id>0 && !closed) {
if(force)
::kill(-data.id, SIGTERM);
else
::kill(-data.id, SIGINT);
}
close_mutex.unlock();
}
void Process::kill(id_type id, bool force) {
if(id<=0)
return;
if(force)
::kill(-id, SIGTERM);
else
::kill(-id, SIGINT);
}

281
src/process_win.cpp

@ -1,281 +0,0 @@
#include "process.hpp"
#include <cstring>
#include "TlHelp32.h"
#include <iostream> //TODO: remove
using namespace std; //TODO: remove
Process::Data::Data(): id(0), handle(NULL) {}
//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx.
Process::id_type Process::open(const std::string &command, const std::string &path) {
if(open_stdin)
stdin_fd=std::unique_ptr<fd_type>(new fd_type);
if(read_stdout)
stdout_fd=std::unique_ptr<fd_type>(new fd_type);
if(read_stderr)
stderr_fd=std::unique_ptr<fd_type>(new fd_type);
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if(stdin_fd) {
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
return 0;
if(!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) {
CloseHandle(g_hChildStd_IN_Rd);
CloseHandle(g_hChildStd_IN_Wr);
return 0;
}
}
if(stdout_fd) {
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd);
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr);
return 0;
}
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd);
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr);
CloseHandle(g_hChildStd_OUT_Rd);
CloseHandle(g_hChildStd_OUT_Wr);
return 0;
}
}
if(stderr_fd) {
if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) {
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd);
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr);
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Rd);
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr);
return 0;
}
if(!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) {
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd);
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr);
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Rd);
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Rd);
CloseHandle(g_hChildStd_ERR_Wr);
return 0;
}
}
PROCESS_INFORMATION process_info;
STARTUPINFO siStartInfo;
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
if(stdin_fd) siStartInfo.hStdInput = g_hChildStd_IN_Rd;
if(stdout_fd) siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
if(stderr_fd) siStartInfo.hStdError = g_hChildStd_ERR_Wr;
if(stdin_fd || stdout_fd || stderr_fd)
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
char* path_cstr;
if(path=="")
path_cstr=NULL;
else {
path_cstr=new char[path.size()+1];
std::strcpy(path_cstr, path.c_str());
}
char* command_cstr;
#ifdef MSYS_PROCESS_USE_SH
size_t pos=0;
std::string sh_command=command;
while((pos=sh_command.find('\\', pos))!=std::string::npos) {
sh_command.replace(pos, 1, "\\\\\\\\");
pos+=4;
}
pos=0;
while((pos=sh_command.find('\"', pos))!=std::string::npos) {
sh_command.replace(pos, 1, "\\\"");
pos+=2;
}
sh_command.insert(0, "sh -c \"");
sh_command+="\"";
command_cstr=new char[sh_command.size()+1];
std::strcpy(command_cstr, sh_command.c_str());
#else
command_cstr=new char[command.size()+1];
std::strcpy(command_cstr, command.c_str());
#endif
BOOL bSuccess = CreateProcess(NULL, command_cstr, NULL, NULL, TRUE, 0,
NULL, path_cstr, &siStartInfo, &process_info);
delete[] path_cstr;
delete[] command_cstr;
if(!bSuccess) {
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd);
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr);
if(stderr_fd) CloseHandle(g_hChildStd_ERR_Wr);
return 0;
}
else {
CloseHandle(process_info.hThread);
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd);
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr);
if(stderr_fd) CloseHandle(g_hChildStd_ERR_Wr);
}
if(stdin_fd) *stdin_fd=g_hChildStd_IN_Wr;
if(stdout_fd) *stdout_fd=g_hChildStd_OUT_Rd;
if(stderr_fd) *stderr_fd=g_hChildStd_ERR_Rd;
closed=false;
data.id=process_info.dwProcessId;
data.handle=process_info.hProcess;
return process_info.dwProcessId;
}
void Process::async_read() {
if(data.id==0)
return;
if(stdout_fd) {
stdout_thread=std::thread([this](){
DWORD n;
char buffer[buffer_size];
for (;;) {
BOOL bSuccess = ReadFile(*stdout_fd, static_cast<CHAR*>(buffer), static_cast<DWORD>(buffer_size), &n, NULL);
if(!bSuccess || n == 0)
break;
read_stdout(buffer, static_cast<size_t>(n));
}
});
}
if(stderr_fd) {
stderr_thread=std::thread([this](){
DWORD n;
char buffer[buffer_size];
for (;;) {
BOOL bSuccess = ReadFile(*stderr_fd, static_cast<CHAR*>(buffer), static_cast<DWORD>(buffer_size), &n, NULL);
if(!bSuccess || n == 0)
break;
read_stderr(buffer, static_cast<size_t>(n));
}
});
}
}
int Process::get_exit_status() {
if(data.id==0)
return -1;
DWORD exit_status;
WaitForSingleObject(data.handle, INFINITE);
if(!GetExitCodeProcess(data.handle, &exit_status))
exit_status=-1;
close_mutex.lock();
CloseHandle(data.handle);
closed=true;
close_mutex.unlock();
close_fds();
return static_cast<int>(exit_status);
}
void Process::close_fds() {
if(stdout_thread.joinable())
stdout_thread.join();
if(stderr_thread.joinable())
stderr_thread.join();
if(stdin_fd)
close_stdin();
if(stdout_fd) {
CloseHandle(*stdout_fd);
stdout_fd.reset();
}
if(stderr_fd) {
CloseHandle(*stderr_fd);
stderr_fd.reset();
}
}
bool Process::write(const char *bytes, size_t n) {
stdin_mutex.lock();
if(stdin_fd) {
DWORD written;
BOOL bSuccess=WriteFile(*stdin_fd, bytes, static_cast<DWORD>(n), &written, NULL);
if(!bSuccess || written==0) {
stdin_mutex.unlock();
return false;
}
else {
stdin_mutex.unlock();
return true;
}
}
stdin_mutex.unlock();
return false;
}
void Process::close_stdin() {
stdin_mutex.lock();
if(stdin_fd) {
CloseHandle(*stdin_fd);
stdin_fd.reset();
}
stdin_mutex.unlock();
}
//Based on http://stackoverflow.com/a/1173396
void Process::kill(bool force) {
close_mutex.lock();
if(data.id>0 && !closed) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(snapshot) {
PROCESSENTRY32 process;
ZeroMemory(&process, sizeof(process));
process.dwSize = sizeof(process);
if(Process32First(snapshot, &process)) {
do {
if(process.th32ParentProcessID==data.id) {
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
if(process_handle) TerminateProcess(process_handle, 2);
}
} while (Process32Next(snapshot, &process));
}
}
TerminateProcess(data.handle, 2);
}
close_mutex.unlock();
}
//Based on http://stackoverflow.com/a/1173396
void Process::kill(id_type id, bool force) {
if(id==0)
return;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(snapshot) {
PROCESSENTRY32 process;
ZeroMemory(&process, sizeof(process));
process.dwSize = sizeof(process);
if(Process32First(snapshot, &process)) {
do {
if(process.th32ParentProcessID==id) {
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
if(process_handle) TerminateProcess(process_handle, 2);
}
} while (Process32Next(snapshot, &process));
}
}
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
if(process_handle) TerminateProcess(process_handle, 2);
}
Loading…
Cancel
Save