Skip to content
This repository was archived by the owner on Jun 25, 2020. It is now read-only.
11 changes: 7 additions & 4 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ if(UNIX) #Checking if compiling on Ubuntu that has a buggy menu system
endif()

if(MSYS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DJUCI_CMAKE_INSTALL_PREFIX=\\\"${CMAKE_INSTALL_PREFIX}\\\"")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMSYS_PROCESS_USE_SH -DJUCI_CMAKE_INSTALL_PREFIX=\\\"${CMAKE_INSTALL_PREFIX}\\\"")
endif()

INCLUDE(FindPkgConfig)
Expand Down Expand Up @@ -66,13 +66,16 @@ set(source_files juci.h
directories.h
directories.cc
terminal.h
terminal.cc
tooltips.h
tooltips.cc
singletons.h
singletons.cc
cmake.h
cmake.cc
dialogs.cc
process.h
process.cc

../libclangmm/src/CodeCompleteResults.cc
../libclangmm/src/CompilationDatabase.cc
Expand All @@ -90,10 +93,10 @@ set(source_files juci.h
../libclangmm/src/Utility.cc)

if(MSYS)
list(APPEND source_files terminal_win.cc)
list(APPEND source_files dialogs_win.cc)
list(APPEND source_files process_win.cc)
list(APPEND source_files dialogs_unix.cc) #dialogs_win.cc does not work any more because of missing SHCreateItemFromParsingName
else()
list(APPEND source_files terminal_unix.cc)
list(APPEND source_files process_unix.cc)
list(APPEND source_files dialogs_unix.cc)
endif()

Expand Down
2 changes: 1 addition & 1 deletion src/cmake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ CMake::CMake(const boost::filesystem::path &path) {

bool CMake::create_compile_commands(const boost::filesystem::path &path) {
Dialog::Message message("Creating "+path.string()+"/compile_commands.json");
auto exit_code=Singleton::terminal->execute(Singleton::config->terminal.cmake_command+" . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path);
auto exit_code=Singleton::terminal->process(Singleton::config->terminal.cmake_command+" . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", path);
message.hide();
if(exit_code==EXIT_SUCCESS) {
#ifdef _WIN32 //Temporary fix to MSYS2's libclang
Expand Down
2 changes: 1 addition & 1 deletion src/juci.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void Application::on_activate() {
}
std::thread another_juci_app([this, directory, files_in_directory](){
Singleton::terminal->async_print("Executing: juci "+directory.string()+files_in_directory);
Singleton::terminal->execute("juci "+directory.string()+files_in_directory, "", false);
Singleton::terminal->process("juci "+directory.string()+files_in_directory, "", false);
});
another_juci_app.detach();
}
Expand Down
25 changes: 25 additions & 0 deletions src/process.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "process.h"

#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):
read_stdout(read_stdout), read_stderr(read_stderr), open_stdin(open_stdin), buffer_size(buffer_size) {
id=open(command, path);
if(id>0)
async_read();
}

Process::~Process() {
if(stdout_thread.joinable())
stdout_thread.join();
if(stderr_thread.joinable())
stderr_thread.join();
}

bool Process::write(const std::string &data) {
return write(data.c_str(), data.size());
}
65 changes: 65 additions & 0 deletions src/process.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef JUCI_PROCESS_H_
#define JUCI_PROCESS_H_

#include <string>
#include <functional>
#include <vector>
#include <mutex>
#include <thread>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/wait.h>
#endif

///Create a new process given command and run path.
///Note: on Windows it seems impossible to specify which pipes to use.
///Thus, 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
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() {return id;}
///Wait until process is finished, and return exit_code.
int get_exit_code();
///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 a given process id.
static void kill(id_type id, bool force=false);

private:
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);
id_type id;
void async_read();
};

#endif // JUCI_PROCESS_H_
159 changes: 159 additions & 0 deletions src/process_unix.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#include "process.h"
#include <cstdlib>
#include <unistd.h>
#include <signal.h>

#include <iostream> //TODO: remove
using namespace std; //TODO: remove

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);

perror("execl");
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];

return pid;
}

void Process::async_read() {
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_code() {
if(id<=0)
return -1;
int exit_code;
waitpid(id, &exit_code, 0);

if(stdout_thread.joinable())
stdout_thread.join();
if(stderr_thread.joinable())
stderr_thread.join();

close_stdin();
if(stdout_fd) {
close(*stdout_fd);
stdout_fd.reset();
}
if(stderr_fd) {
close(*stderr_fd);
stderr_fd.reset();
}

return exit_code;
}

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(id_type id, bool force) {
if(id<=0)
return;
if(force)
::kill(-id, SIGTERM);
else
::kill(-id, SIGINT);
}
Loading