// #include #ifdef _WIN32 #include #include #include #include #include #include #else #include #include #include #include #include #include #include #include #include #include #include #include #endif // Special key codes (unified across platforms) #define KEY_ARROW_UP 1001 #define KEY_ARROW_DOWN 1002 #define KEY_ARROW_RIGHT 1003 #define KEY_ARROW_LEFT 1004 int terminkey(); void echooff(); void echoon(); int termwidth(); int ipc_init(); int ipc_check_message(uintptr_t buffer_ptr, int max_len); void ipc_send_message(const char* message); void ipc_cleanup(); #ifdef _WIN32 static HANDLE hServerPipe = INVALID_HANDLE_VALUE; static OVERLAPPED oOverlap; static BOOL fPendingIO = FALSE; int terminkey() { if (_kbhit()) { int ch = _getch(); // Handle Windows arrow keys if (ch == 224) { int next = _getch(); switch (next) { case 72: return KEY_ARROW_UP; case 80: return KEY_ARROW_DOWN; case 75: return KEY_ARROW_LEFT; case 77: return KEY_ARROW_RIGHT; default: return next; } } return ch; } return -1; } void echooff() { HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; GetConsoleMode(hStdin, &mode); SetConsoleMode(hStdin, mode & ~ENABLE_ECHO_INPUT); } void echoon() { HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); DWORD mode = 0; GetConsoleMode(hStdin, &mode); SetConsoleMode(hStdin, mode | ENABLE_ECHO_INPUT); } int termwidth() { HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; if (GetConsoleScreenBufferInfo(hStdout, &csbi)) { return csbi.srWindow.Right - csbi.srWindow.Left + 1; } return -1; } int ipc_init() { // Create a regular blocking pipe, but enable FILE_FLAG_OVERLAPPED for safe async I/O hServerPipe = CreateNamedPipe( "\\\\.\\pipe\\cimp_ipc", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 4096, 4096, 0, NULL ); if (hServerPipe == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); if (err == ERROR_ACCESS_DENIED || err == ERROR_PIPE_BUSY) { return 0; // Already running (Client Mode) } return -1; // Other error } // Initialize the overlapped structure and create an event memset(&oOverlap, 0, sizeof(OVERLAPPED)); oOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); fPendingIO = FALSE; return 1; // Server mode successfully started } int ipc_check_message(uintptr_t buffer_ptr, int max_len) { if (hServerPipe == INVALID_HANDLE_VALUE) return 0; char* buffer = (char*)buffer_ptr; DWORD bytesRead = 0; if (!fPendingIO) { // Start an asynchronous listen operation if (!ConnectNamedPipe(hServerPipe, &oOverlap)) { DWORD err = GetLastError(); if (err == ERROR_IO_PENDING) { fPendingIO = TRUE; } else if (err == ERROR_PIPE_CONNECTED) { // Client already connected before we listened! SetEvent(oOverlap.hEvent); fPendingIO = TRUE; } } } if (fPendingIO) { // Check if the connection event has triggered without waiting (0ms timeout) if (WaitForSingleObject(oOverlap.hEvent, 0) == WAIT_OBJECT_0) { DWORD cbRet = 0; // Connection is ready, let's see if there is data if (GetOverlappedResult(hServerPipe, &oOverlap, &cbRet, FALSE)) { DWORD bytesAvail = 0; if (PeekNamedPipe(hServerPipe, NULL, 0, NULL, &bytesAvail, NULL) && bytesAvail > 0) { // Data has arrived, read it synchronously BOOL success = ReadFile(hServerPipe, buffer, max_len - 1, &bytesRead, NULL); if (success && bytesRead > 0) { buffer[bytesRead] = '\0'; // Clean up and reset for the next client connection DisconnectNamedPipe(hServerPipe); ResetEvent(oOverlap.hEvent); fPendingIO = FALSE; return (int)bytesRead; } } } else { // Client disconnected or pipe broke before sending DisconnectNamedPipe(hServerPipe); ResetEvent(oOverlap.hEvent); fPendingIO = FALSE; } } } return 0; } void ipc_send_message(const char* message) { HANDLE hPipe = CreateFile( "\\\\.\\pipe\\cimp_ipc", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); if (hPipe != INVALID_HANDLE_VALUE) { DWORD bytesWritten = 0; WriteFile(hPipe, message, strlen(message), &bytesWritten, NULL); CloseHandle(hPipe); } } void ipc_cleanup() { if (hServerPipe != INVALID_HANDLE_VALUE) { DisconnectNamedPipe(hServerPipe); CloseHandle(hServerPipe); hServerPipe = INVALID_HANDLE_VALUE; } if (oOverlap.hEvent != NULL) { CloseHandle(oOverlap.hEvent); oOverlap.hEvent = NULL; } } #else static int server_fd = -1; static char socket_path[256] = ""; static void get_socket_path() { if (socket_path[0] == '\0') { uid_t uid = getuid(); snprintf(socket_path, sizeof(socket_path), "/tmp/cimp_ipc_%u.sock", (unsigned int)uid); } } int terminkey() { struct termios oldt, newt; int ch; int oldf; // Save existing terminal settings tcgetattr(STDIN_FILENO, &oldt); newt = oldt; // Disable buffering and echoing newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &newt); // Set STDIN to non-blocking oldf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); ch = getchar(); // Handle Unix arrow keys (ESC [ A/B/C/D) if (ch == 27) { int next1 = getchar(); if (next1 == '[') { int next2 = getchar(); switch (next2) { case 'A': ch = KEY_ARROW_UP; break; case 'B': ch = KEY_ARROW_DOWN; break; case 'C': ch = KEY_ARROW_RIGHT; break; case 'D': ch = KEY_ARROW_LEFT; break; } } } // Restore terminal settings tcsetattr(STDIN_FILENO, TCSANOW, &oldt); fcntl(STDIN_FILENO, F_SETFL, oldf); return ch; } void echooff() { struct termios tty; tcgetattr(STDIN_FILENO, &tty); tty.c_lflag &= ~ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &tty); } void echoon() { struct termios tty; tcgetattr(STDIN_FILENO, &tty); tty.c_lflag |= ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &tty); } int termwidth() { struct winsize w; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) { return w.ws_col; } return -1; } int ipc_init() { get_socket_path(); int test_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (test_fd >= 0) { struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); if (connect(test_fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { close(test_fd); return 0; // Already running } close(test_fd); } unlink(socket_path); server_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (server_fd < 0) { return -1; } int flags = fcntl(server_fd, F_GETFL, 0); fcntl(server_fd, F_SETFL, flags | O_NONBLOCK); struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { close(server_fd); server_fd = -1; return -1; } if (listen(server_fd, 5) < 0) { close(server_fd); server_fd = -1; unlink(socket_path); return -1; } return 1; } int ipc_check_message(uintptr_t buffer_ptr, int max_len) { if (server_fd < 0) return 0; char* buffer = (char*)buffer_ptr; int client_fd = accept(server_fd, NULL, NULL); if (client_fd < 0) { return 0; } int total_read = 0; while (total_read < max_len - 1) { int r = read(client_fd, buffer + total_read, max_len - 1 - total_read); if (r <= 0) break; total_read += r; } buffer[total_read] = '\0'; close(client_fd); return total_read; } void ipc_send_message(const char* message) { get_socket_path(); int client_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (client_fd < 0) return; struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); if (connect(client_fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { int total_written = 0; int len = strlen(message); while (total_written < len) { int w = write(client_fd, message + total_written, len - total_written); if (w <= 0) break; total_written += w; } } close(client_fd); } void ipc_cleanup() { if (server_fd >= 0) { close(server_fd); server_fd = -1; } get_socket_path(); unlink(socket_path); } #endif