#ifdef HAVE_CONFIG_H
# include "config_auto.h"
#endif
#include "svutil.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#ifdef _WIN32
# pragma comment(lib, "Ws2_32.lib")
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <arpa/inet.h>
# include <netdb.h>
# include <netinet/in.h>
# include <semaphore.h>
# include <sys/select.h>
# include <sys/socket.h>
# include <csignal>
# ifdef __linux__
# include <sys/prctl.h>
# endif
# include <unistd.h>
#endif
#if defined(_WIN32) && !defined(__GNUC__)
# define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr)
#endif
#ifndef GRAPHICS_DISABLED
namespace tesseract {
void SVSync::StartProcess(const char *executable, const char *args) {
std::string proc;
proc.append(executable);
proc.append(" ");
proc.append(args);
std::cout << "Starting " << proc << std::endl;
# ifdef _WIN32
STARTUPINFO start_info;
PROCESS_INFORMATION proc_info;
GetStartupInfo(&start_info);
if (!CreateProcess(nullptr, const_cast<char *>(proc.c_str()), nullptr,
nullptr, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS,
nullptr, nullptr, &start_info, &proc_info))
return;
# else
int pid = fork();
if (pid != 0) {
} else {
# ifdef __linux__
prctl(PR_SET_PDEATHSIG, 2, 0, 0, 0);
# endif
std::string mutable_args(args);
int argc = 1;
for (auto ch : mutable_args) {
if (ch == ' ') {
++argc;
}
}
std::unique_ptr<char *[]> argv(new char *[argc + 2]);
std::string argv0(executable);
argv[0] = &argv0[0];
argv[1] = &mutable_args[0];
argc = 2;
bool inquote = false;
for (int i = 0; mutable_args[i]; ++i) {
if (!inquote && mutable_args[i] == ' ') {
mutable_args[i] = '\0';
argv[argc++] = &mutable_args[i + 1];
} else if (mutable_args[i] == '"') {
inquote = !inquote;
mutable_args[i] = ' ';
}
}
argv[argc] = nullptr;
execvp(executable, argv.get());
}
# endif
}
SVSemaphore::SVSemaphore() {
# ifdef _WIN32
semaphore_ = CreateSemaphore(0, 0, 10, 0);
# elif defined(__APPLE__)
auto name = std::to_string(random());
sem_unlink(name.c_str());
semaphore_ = sem_open(name.c_str(), O_CREAT, S_IWUSR, 0);
if (semaphore_ == SEM_FAILED) {
perror("sem_open");
}
# else
sem_init(&semaphore_, 0, 0);
# endif
}
SVSemaphore::~SVSemaphore() {
# ifdef _WIN32
CloseHandle(semaphore_);
# elif defined(__APPLE__)
sem_close(semaphore_);
# else
sem_close(&semaphore_);
# endif
}
void SVSemaphore::Signal() {
# ifdef _WIN32
ReleaseSemaphore(semaphore_, 1, nullptr);
# elif defined(__APPLE__)
sem_post(semaphore_);
# else
sem_post(&semaphore_);
# endif
}
void SVSemaphore::Wait() {
# ifdef _WIN32
WaitForSingleObject(semaphore_, INFINITE);
# elif defined(__APPLE__)
sem_wait(semaphore_);
# else
sem_wait(&semaphore_);
# endif
}
void SVNetwork::Send(const char *msg) {
std::lock_guard<std::mutex> guard(mutex_send_);
msg_buffer_out_.append(msg);
}
void SVNetwork::Flush() {
std::lock_guard<std::mutex> guard(mutex_send_);
while (!msg_buffer_out_.empty()) {
int i = send(stream_, msg_buffer_out_.c_str(), msg_buffer_out_.length(), 0);
msg_buffer_out_.erase(0, i);
}
}
char *SVNetwork::Receive() {
char *result = nullptr;
if (buffer_ptr_ != nullptr) {
result = strtok_r(nullptr, "\n", &buffer_ptr_);
}
if (result != nullptr) {
return result;
} else {
buffer_ptr_ = nullptr;
struct timeval tv;
tv.tv_sec = 10;
tv.tv_usec = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(stream_, &readfds);
int i = select(stream_ + 1, &readfds, nullptr, nullptr, &tv);
if (i == 0) {
return nullptr;
}
i = recv(stream_, msg_buffer_in_, kMaxMsgSize, 0);
if (i <= 0) {
return nullptr;
}
msg_buffer_in_[i] = '\0';
return strtok_r(msg_buffer_in_, "\n", &buffer_ptr_);
}
}
void SVNetwork::Close() {
# ifdef _WIN32
closesocket(stream_);
# else
close(stream_);
# endif
stream_ = -1;
}
static const char *ScrollViewProg() {
# ifdef _WIN32
const char *prog = "java -Xms512m -Xmx1024m";
# else
const char *prog = "sh";
# endif
return prog;
}
static std::string ScrollViewCommand(const std::string &scrollview_path) {
# ifdef _WIN32
const char cmd_template[] =
"-Djava.library.path=\"%s\" -jar \"%s/ScrollView.jar\"";
# else
const char cmd_template[] =
"-c \"trap 'kill %%1' 0 1 2 ; java "
"-Xms1024m -Xmx2048m -jar %s/ScrollView.jar"
" & wait\"";
# endif
size_t cmdlen = sizeof(cmd_template) + 2 * scrollview_path.size() + 1;
std::vector<char> cmd(cmdlen);
const char *sv_path = scrollview_path.c_str();
# ifdef _WIN32
snprintf(&cmd[0], cmdlen, cmd_template, sv_path, sv_path);
# else
snprintf(&cmd[0], cmdlen, cmd_template, sv_path);
# endif
std::string command(&cmd[0]);
return command;
}
SVNetwork::SVNetwork(const char *hostname, int port) {
msg_buffer_in_ = new char[kMaxMsgSize + 1];
msg_buffer_in_[0] = '\0';
buffer_ptr_ = nullptr;
auto port_string = std::to_string(port);
# ifdef _WIN32
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::cerr << "WSAStartup failed: " << iResult << std::endl;
}
# endif
struct addrinfo *addr_info = nullptr;
struct addrinfo hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(hostname, port_string.c_str(), &hints, &addr_info) != 0) {
std::cerr << "Error resolving name for ScrollView host "
<< std::string(hostname) << ":" << port << std::endl;
# ifdef _WIN32
WSACleanup();
# endif
}
if (addr_info == nullptr) {
stream_ = -1;
} else {
stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
addr_info->ai_protocol);
}
if (stream_ < 0) {
std::cerr << "Failed to open socket" << std::endl;
} else if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) < 0) {
const char *scrollview_path = getenv("SCROLLVIEW_PATH");
if (scrollview_path == nullptr) {
# ifdef SCROLLVIEW_PATH
# define _STR(a) # a
# define _XSTR(a) _STR(a)
scrollview_path = _XSTR(SCROLLVIEW_PATH);
# undef _XSTR
# undef _STR
# else
scrollview_path = ".";
# endif
}
const char *prog = ScrollViewProg();
std::string command = ScrollViewCommand(scrollview_path);
SVSync::StartProcess(prog, command.c_str());
Close();
for (;;) {
stream_ = socket(addr_info->ai_family, addr_info->ai_socktype,
addr_info->ai_protocol);
if (stream_ >= 0) {
if (connect(stream_, addr_info->ai_addr, addr_info->ai_addrlen) == 0) {
break;
}
Close();
std::cout << "ScrollView: Waiting for server...\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
}
# ifdef _WIN32
# endif
freeaddrinfo(addr_info);
}
SVNetwork::~SVNetwork() {
Close();
delete[] msg_buffer_in_;
}
}
#endif