Compare commits

...

8 Commits

Author SHA1 Message Date
Mario Fetka
3890b78c3f build: replace ncurses dependency with local FTX shim 2026-06-13 11:10:49 +02:00
Mario Fetka
b6764d4d4b test: isolate nwflaim smoke test work directory 2026-06-13 09:49:11 +02:00
Mario Fetka
145f505b09 update 2026-06-09 19:11:48 +02:00
OpenAI Build Bot
a8f9e7e9ab Treat base64 key EOF as successful decode 2026-06-05 21:56:22 +02:00
OpenAI Build Bot
b02c348124 Handle missing dictionary-name keys as no conflict 2026-06-05 21:48:59 +02:00
Mario Fetka
4c3f2afecf Scope NICI support to classic FLAIM targets 2026-06-05 21:18:02 +02:00
OpenAI
4ddfe31a5a Disable experimental FlaimSQL by default 2026-06-05 20:29:39 +02:00
OpenAI Build Bot
a63783ddd2 Link FlaimSQL against core FLAIM library 2026-06-05 20:07:51 +02:00
6 changed files with 301 additions and 67 deletions

0
.gitignore vendored Normal file
View File

View File

@@ -6,7 +6,7 @@ include(CTest)
option(NWFLAIM_BUILD_SHARED "Build shared nwflaim libraries" ON)
option(NWFLAIM_BUILD_STATIC "Build static nwflaim libraries" OFF)
option(NWFLAIM_BUILD_SQL "Build SQL FLAIM library" ON)
option(NWFLAIM_BUILD_SQL "Build experimental SQL FLAIM library" OFF)
option(NWFLAIM_BUILD_XFLAIM "Build XML FLAIM library" ON)
option(NWFLAIM_BUILD_TOOLS "Build installable FLAIM command line tools with nw-prefixed names" ON)
option(NWFLAIM_WITH_OPENSSL "Build FTK legacy OpenSSL/TLS helpers" ON)
@@ -38,42 +38,18 @@ set(_nwflaim_common_defs
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND _nwflaim_common_defs FLM_64BIT)
endif()
if(NWFLAIM_USE_NICI)
list(APPEND _nwflaim_common_defs FLM_USE_NICI)
endif()
# FLM_USE_NICI is intentionally not part of the global/default define set.
# Classic FLAIM and its own tools use the NICI/CCS encrypted-storage path,
# but XFLAIM has a separate NICI implementation with a different historical
# API surface. Enabling FLM_USE_NICI globally makes xflaim/src/f_nici.cpp
# compile against the classic compatibility headers and breaks the default
# mars-nwe build.
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: <none reported>")
endif()
if(CURSES_LIBRARIES)
message(STATUS "NWFLAIM: curses libraries: ${CURSES_LIBRARIES}")
else()
message(STATUS "NWFLAIM: curses libraries: <none reported>")
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)
@@ -126,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
@@ -367,6 +339,16 @@ set(NWFLAIM_XFLAIM_SOURCES
xflaim/src/scache.cpp
xflaim/src/translog.cpp)
function(nwflaim_enable_classic_nici target_name)
if(NWFLAIM_USE_NICI AND TARGET ${target_name})
target_compile_definitions(${target_name} PRIVATE FLM_USE_NICI)
if(NOT NWFLAIM_NICI_INCLUDE_DIR STREQUAL "")
target_include_directories(${target_name} PRIVATE "${NWFLAIM_NICI_INCLUDE_DIR}")
endif()
endif()
endfunction()
function(nwflaim_common_target target_name public_include_dir install_include_dir)
target_compile_features(${target_name} PUBLIC cxx_std_98)
target_compile_definitions(${target_name} PRIVATE ${_nwflaim_common_defs})
@@ -379,18 +361,8 @@ function(nwflaim_common_target target_name public_include_dir install_include_di
"${CMAKE_CURRENT_SOURCE_DIR}/flaim/src"
"${CMAKE_CURRENT_SOURCE_DIR}/sql/src"
"${CMAKE_CURRENT_SOURCE_DIR}/xflaim/src")
if(NWFLAIM_USE_NICI AND NOT NWFLAIM_NICI_INCLUDE_DIR STREQUAL "")
target_include_directories(${target_name} PRIVATE "${NWFLAIM_NICI_INCLUDE_DIR}")
endif()
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()
@@ -411,18 +383,8 @@ function(nwflaim_common_private_target target_name)
"${CMAKE_CURRENT_SOURCE_DIR}/sql/src"
"${CMAKE_CURRENT_SOURCE_DIR}/xflaim/src"
"${CMAKE_CURRENT_SOURCE_DIR}/xflaim/util")
if(NWFLAIM_USE_NICI AND NOT NWFLAIM_NICI_INCLUDE_DIR STREQUAL "")
target_include_directories(${target_name} PRIVATE "${NWFLAIM_NICI_INCLUDE_DIR}")
endif()
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()
@@ -478,9 +440,11 @@ endif()
nwflaim_add_library(flaim flaim flaim/src "${CMAKE_INSTALL_INCLUDEDIR}/nwflaim" "${NWFLAIM_FLAIM_LIBRARY_VERSION}" "${NWFLAIM_FLAIM_LIBRARY_SOVERSION}" ${NWFLAIM_FLAIM_SOURCES})
if(TARGET flaim)
target_link_libraries(flaim PUBLIC flaimtk)
nwflaim_enable_classic_nici(flaim)
endif()
if(TARGET flaim_static)
target_link_libraries(flaim_static PUBLIC flaimtk_static)
nwflaim_enable_classic_nici(flaim_static)
endif()
if(NWFLAIM_BUILD_SQL)
@@ -510,17 +474,21 @@ if(NWFLAIM_BUILD_TOOLS)
flaim/util/flm_lutl.cpp
flaim/util/sharutil.cpp)
nwflaim_common_private_target(flaim_util_objects)
nwflaim_enable_classic_nici(flaim_util_objects)
target_include_directories(flaim_util_objects PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/flaim/util")
nwflaim_add_tool(flmcheckdb_tool nwflmcheckdb flaim
flaim/util/checkdb.cpp
$<TARGET_OBJECTS:flaim_util_objects>)
nwflaim_enable_classic_nici(flmcheckdb_tool)
nwflaim_add_tool(flmrebuild_tool nwflmrebuild flaim
flaim/util/rebuild.cpp
$<TARGET_OBJECTS:flaim_util_objects>)
nwflaim_enable_classic_nici(flmrebuild_tool)
nwflaim_add_tool(flmgigatest_tool nwflmgigatest flaim
flaim/util/gigatest.cpp
$<TARGET_OBJECTS:flaim_util_objects>)
nwflaim_enable_classic_nici(flmgigatest_tool)
nwflaim_add_tool(flmview_tool nwflmview flaim
flaim/util/viewblk.cpp
flaim/util/view.cpp
@@ -532,10 +500,12 @@ if(NWFLAIM_BUILD_TOOLS)
flaim/util/viewmenu.cpp
flaim/util/viewsrch.cpp
$<TARGET_OBJECTS:flaim_util_objects>)
nwflaim_enable_classic_nici(flmview_tool)
nwflaim_add_tool(flmdbshell_tool nwflmdbshell flaim
flaim/util/dbshell.cpp
flaim/util/flm_edit.cpp
$<TARGET_OBJECTS:flaim_util_objects>)
nwflaim_enable_classic_nici(flmdbshell_tool)
if(NWFLAIM_BUILD_XFLAIM)
add_library(xflaim_util_objects OBJECT

View File

@@ -9,12 +9,19 @@ if(NOT DEFINED NWFLAIM_TEST_WORKDIR)
endif()
set(ENV{TERM} xterm)
file(REMOVE_RECURSE "${NWFLAIM_TEST_WORKDIR}")
get_filename_component(_nwflaim_work_parent "${NWFLAIM_TEST_WORKDIR}" DIRECTORY)
get_filename_component(_nwflaim_work_name "${NWFLAIM_TEST_WORKDIR}" NAME)
string(RANDOM LENGTH 8 ALPHABET 0123456789abcdef _nwflaim_work_suffix)
file(MAKE_DIRECTORY "${_nwflaim_work_parent}")
set(_nwflaim_effective_workdir
"${_nwflaim_work_parent}/${_nwflaim_work_name}-${_nwflaim_work_suffix}")
file(REMOVE_RECURSE "${_nwflaim_effective_workdir}")
file(MAKE_DIRECTORY
"${NWFLAIM_TEST_WORKDIR}"
"${NWFLAIM_TEST_WORKDIR}/data"
"${NWFLAIM_TEST_WORKDIR}/rfl"
"${NWFLAIM_TEST_WORKDIR}/tmp")
"${_nwflaim_effective_workdir}"
"${_nwflaim_effective_workdir}/data"
"${_nwflaim_effective_workdir}/rfl"
"${_nwflaim_effective_workdir}/tmp")
set(NWFLAIM_TEST_WORKDIR "${_nwflaim_effective_workdir}")
set(_db "${NWFLAIM_TEST_WORKDIR}/gigatest.db")
set(_records 256)
set(_stdin "${NWFLAIM_TEST_WORKDIR}/stdin.txt")

View File

@@ -1539,7 +1539,15 @@ RCODE F_CCS::setKeyFromStore(
if( RC_BAD( rc = B64Decoder.read(
(void *)pucKeyBuf, ui32BufLen, &uiLength)))
{
goto Exit;
if( rc != FERR_EOF_HIT)
{
goto Exit;
}
// F_Base64DecoderIStream signals end-of-stream with EOF after
// returning the decoded bytes. In this path EOF is a successful
// completion, not a failed key decode.
rc = FERR_OK;
}
pucTmp = pucKeyBuf;
}

View File

@@ -2508,7 +2508,16 @@ FSTATIC RCODE DDCheckNameConflict(
if (RC_BAD( rc = FSBtSearch( pDb, pDictIxLFile, &pStack,
IxKeyBuf, uiKeyLen, 0L)))
{
goto Exit;
if (rc == FERR_EOF_HIT)
{
// No matching dictionary-name key exists yet, so there is no
// name conflict. Treat end-of-index as a successful miss.
rc = FERR_OK;
}
else
{
goto Exit;
}
}
if (pStack->uiCmpStatus == BT_EQ_KEY)

240
ftk/src/curses.h Normal file
View File

@@ -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 <sys/ioctl.h>
#include <sys/select.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#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