From 3890b78c3fdceba996a27ee7443101c865cd14a7 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Sat, 13 Jun 2026 09:07:48 +0000 Subject: [PATCH] build: replace ncurses dependency with local FTX shim --- CMakeLists.txt | 55 +---------- ftk/src/curses.h | 240 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+), 50 deletions(-) create mode 100644 ftk/src/curses.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 65576c3..65498b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,35 +48,8 @@ endif() find_package(Threads REQUIRED) find_library(NWFLAIM_RT_LIBRARY rt) find_library(NWFLAIM_DL_LIBRARY dl) -find_package(Curses QUIET) -if(CURSES_FOUND) - set(_nwflaim_curses_include_dirs "") - if(CURSES_INCLUDE_DIRS) - set(_nwflaim_curses_include_dirs "${CURSES_INCLUDE_DIRS}") - elseif(CURSES_INCLUDE_DIR) - set(_nwflaim_curses_include_dirs "${CURSES_INCLUDE_DIR}") - endif() - - message(STATUS "NWFLAIM: curses/ncurses found") - if(_nwflaim_curses_include_dirs) - message(STATUS "NWFLAIM: curses include dirs: ${_nwflaim_curses_include_dirs}") - else() - message(STATUS "NWFLAIM: curses include dirs: ") - endif() - if(CURSES_LIBRARIES) - message(STATUS "NWFLAIM: curses libraries: ${CURSES_LIBRARIES}") - else() - message(STATUS "NWFLAIM: curses libraries: ") - endif() - if(NWFLAIM_BUILD_TOOLS) - message(STATUS "NWFLAIM: tools enabled; curses-backed tools will be built") - endif() -else() - message(STATUS "NWFLAIM: curses/ncurses not found") -endif() -if(NWFLAIM_BUILD_TOOLS AND NOT CURSES_FOUND) - message(FATAL_ERROR "NWFLAIM_BUILD_TOOLS requires curses/ncurses") -endif() +message(STATUS "NWFLAIM: using bundled minimal curses compatibility shim; no ncurses dependency") +set(_nwflaim_curses_include_dirs "${CMAKE_CURRENT_SOURCE_DIR}/ftk/src") set(_nwflaim_ssl_target "") if(NWFLAIM_WITH_OPENSSL) @@ -129,11 +102,7 @@ set(NWFLAIM_FTK_SOURCES ftk/src/ftkwin.cpp ftk/src/ftkxml.cpp) -if(CURSES_FOUND) - list(APPEND NWFLAIM_FTK_SOURCES ftk/src/ftkftx.cpp) -else() - message(STATUS "Curses not found; building libnwflaimtk without console FTX helpers") -endif() +list(APPEND NWFLAIM_FTK_SOURCES ftk/src/ftkftx.cpp) set(NWFLAIM_FLAIM_SOURCES flaim/src/checksum.cpp @@ -393,14 +362,7 @@ function(nwflaim_common_target target_name public_include_dir install_include_di "${CMAKE_CURRENT_SOURCE_DIR}/sql/src" "${CMAKE_CURRENT_SOURCE_DIR}/xflaim/src") target_link_libraries(${target_name} PUBLIC Threads::Threads) - if(CURSES_INCLUDE_DIRS) - target_include_directories(${target_name} PRIVATE ${CURSES_INCLUDE_DIRS}) - elseif(CURSES_INCLUDE_DIR) - target_include_directories(${target_name} PRIVATE ${CURSES_INCLUDE_DIR}) - endif() - if(CURSES_LIBRARIES) - target_link_libraries(${target_name} PUBLIC ${CURSES_LIBRARIES}) - endif() + target_include_directories(${target_name} PRIVATE ${_nwflaim_curses_include_dirs}) if(NWFLAIM_RT_LIBRARY) target_link_libraries(${target_name} PUBLIC "${NWFLAIM_RT_LIBRARY}") endif() @@ -422,14 +384,7 @@ function(nwflaim_common_private_target target_name) "${CMAKE_CURRENT_SOURCE_DIR}/xflaim/src" "${CMAKE_CURRENT_SOURCE_DIR}/xflaim/util") target_link_libraries(${target_name} PRIVATE Threads::Threads) - if(CURSES_INCLUDE_DIRS) - target_include_directories(${target_name} PRIVATE ${CURSES_INCLUDE_DIRS}) - elseif(CURSES_INCLUDE_DIR) - target_include_directories(${target_name} PRIVATE ${CURSES_INCLUDE_DIR}) - endif() - if(CURSES_LIBRARIES) - target_link_libraries(${target_name} PRIVATE ${CURSES_LIBRARIES}) - endif() + target_include_directories(${target_name} PRIVATE ${_nwflaim_curses_include_dirs}) if(NWFLAIM_RT_LIBRARY) target_link_libraries(${target_name} PRIVATE "${NWFLAIM_RT_LIBRARY}") endif() diff --git a/ftk/src/curses.h b/ftk/src/curses.h new file mode 100644 index 0000000..822c1b1 --- /dev/null +++ b/ftk/src/curses.h @@ -0,0 +1,240 @@ +#ifndef NWFLAIM_CURSES_COMPAT_H +#define NWFLAIM_CURSES_COMPAT_H + +/* + * Minimal curses-compatible terminal shim for legacy FLAIM FTX tools. + * This is intentionally not a general curses implementation. It only + * provides the subset used by ftkftx.cpp, so mars-flaim can build its + * historical tools without depending on ncurses. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int chtype; +typedef struct nwflaim_curses_window { int dummy; } WINDOW; + +#define ERR (-1) +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define A_NORMAL 0x00000000u +#define A_BOLD 0x01000000u +#define A_REVERSE 0x02000000u +#define COLOR_PAIR(n) ((chtype)((n) << 16)) + +#define COLOR_BLACK 0 +#define COLOR_RED 1 +#define COLOR_GREEN 2 +#define COLOR_YELLOW 3 +#define COLOR_BLUE 4 +#define COLOR_MAGENTA 5 +#define COLOR_CYAN 6 +#define COLOR_WHITE 7 +#define COLOR_PAIRS 64 + +#define KEY_F(n) (0x1000 + (n)) +#define KEY_BACKSPACE 0x1101 +#define KEY_DC 0x1102 +#define KEY_FIND 0x1103 +#define KEY_HOME 0x1104 +#define KEY_END 0x1105 +#define KEY_SELECT 0x1106 +#define KEY_LL 0x1107 +#define KEY_LEFT 0x1108 +#define KEY_RIGHT 0x1109 +#define KEY_DOWN 0x110a +#define KEY_UP 0x110b +#define KEY_ENTER 0x110c +#define KEY_NPAGE 0x110d +#define KEY_PPAGE 0x110e +#define KEY_IC 0x110f + +static WINDOW nwflaim_curses_stdscr_obj = { 0 }; +static WINDOW *stdscr = &nwflaim_curses_stdscr_obj; +static int LINES = 25; +static int COLS = 80; + +static struct termios nwflaim_curses_saved_termios; +static int nwflaim_curses_have_termios = 0; +static int nwflaim_curses_started = 0; +static int nwflaim_curses_cur_fg = 7; +static int nwflaim_curses_cur_bg = 0; +static int nwflaim_curses_pair_fg[COLOR_PAIRS + 1]; +static int nwflaim_curses_pair_bg[COLOR_PAIRS + 1]; + +static void nwflaim_curses_update_size(void) +{ + struct winsize ws; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { + if (ws.ws_row > 0) LINES = ws.ws_row; + if (ws.ws_col > 0) COLS = ws.ws_col; + } +} + +static int nwflaim_curses_read_byte(int wait) +{ + unsigned char ch; + fd_set rfds; + struct timeval tv; + int rc; + + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + tv.tv_sec = wait ? 1 : 0; + tv.tv_usec = wait ? 0 : 0; + rc = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv); + if (rc <= 0) return ERR; + if (read(STDIN_FILENO, &ch, 1) != 1) return ERR; + return (int)ch; +} + +static int initscr(void) +{ + struct termios tio; + nwflaim_curses_update_size(); + if (tcgetattr(STDIN_FILENO, &nwflaim_curses_saved_termios) == 0) { + nwflaim_curses_have_termios = 1; + tio = nwflaim_curses_saved_termios; + tio.c_lflag &= (tcflag_t)~(ICANON | ECHO); + tio.c_iflag &= (tcflag_t)~(IXON | ICRNL); + tio.c_cc[VMIN] = 0; + tio.c_cc[VTIME] = 1; + (void)tcsetattr(STDIN_FILENO, TCSANOW, &tio); + } + nwflaim_curses_started = 1; + fputs("\033[?25l", stdout); + fflush(stdout); + return 0; +} + +static int endwin(void) +{ + if (nwflaim_curses_have_termios) { + (void)tcsetattr(STDIN_FILENO, TCSANOW, &nwflaim_curses_saved_termios); + } + fputs("\033[0m\033[?25h\n", stdout); + fflush(stdout); + nwflaim_curses_started = 0; + return 0; +} + +static int noecho(void) { return 0; } +static int cbreak(void) { return 0; } +static int halfdelay(int tenths) { (void)tenths; return 0; } +static int meta(WINDOW *w, int bf) { (void)w; (void)bf; return 0; } +static int keypad(WINDOW *w, int bf) { (void)w; (void)bf; return 0; } +static int scrollok(WINDOW *w, int bf) { (void)w; (void)bf; return 0; } +static int has_colors(void) { return 1; } +static int start_color(void) { return 0; } +static int bkgd(chtype ch) { (void)ch; return 0; } +static int erasechar(void) { return 0x7f; } +static int killchar(void) { return 0x15; } + +static int init_pair(short pair, short fg, short bg) +{ + if (pair >= 0 && pair <= COLOR_PAIRS) { + nwflaim_curses_pair_fg[pair] = fg; + nwflaim_curses_pair_bg[pair] = bg; + } + return 0; +} + +static void nwflaim_curses_apply_attr(chtype ch) +{ + int pair = (int)((ch >> 16) & 0xff); + int fg = nwflaim_curses_cur_fg; + int bg = nwflaim_curses_cur_bg; + int bold = (ch & A_BOLD) != 0; + int rev = (ch & A_REVERSE) != 0; + if (pair >= 0 && pair <= COLOR_PAIRS) { + fg = nwflaim_curses_pair_fg[pair]; + bg = nwflaim_curses_pair_bg[pair]; + } + if (rev) { + int tmp = fg; fg = bg; bg = tmp; + } + fprintf(stdout, "\033[0;%d;%d;%dm", bold ? 1 : 22, 30 + fg, 40 + bg); +} + +static int addch(chtype ch) +{ + unsigned int c = ch & 0xffu; + nwflaim_curses_apply_attr(ch); + if (c == 0) c = ' '; + fputc((int)c, stdout); + return 0; +} + +static int move(int y, int x) +{ + if (y < 0) y = 0; + if (x < 0) x = 0; + fprintf(stdout, "\033[%d;%dH", y + 1, x + 1); + return 0; +} + +static int refresh(void) +{ + fflush(stdout); + return 0; +} + +static int clearok(WINDOW *w, int bf) +{ + (void)w; + if (bf) fputs("\033[2J\033[H", stdout); + return 0; +} + +static int getch(void) +{ + int c = nwflaim_curses_read_byte(0); + if (c != 0x1b) return c; + + int c2 = nwflaim_curses_read_byte(0); + if (c2 == ERR) return 0x1b; + if (c2 != '[' && c2 != 'O') return c2; + + c = nwflaim_curses_read_byte(0); + switch (c) { + case 'A': return KEY_UP; + case 'B': return KEY_DOWN; + case 'C': return KEY_RIGHT; + case 'D': return KEY_LEFT; + case 'H': return KEY_HOME; + case 'F': return KEY_END; + case '1': return KEY_HOME; + case '2': return KEY_IC; + case '3': return KEY_DC; + case '4': return KEY_END; + case '5': return KEY_PPAGE; + case '6': return KEY_NPAGE; + case 'P': return KEY_F(1); + case 'Q': return KEY_F(2); + case 'R': return KEY_F(3); + case 'S': return KEY_F(4); + default: return ERR; + } +} + +static int wgetch(WINDOW *w) { (void)w; return getch(); } +static int clear(void) { fputs("\033[2J\033[H", stdout); return 0; } + +#ifdef __cplusplus +} +#endif + +#endif