DataReel 6.0 Release Notes, 10/24/2026
DataReel version 6.0 (was 5.10)
DataReel Copyright (c) 2001-2026 DataReel Software Development

DataReel 6.0 is the first major release in many years. Version 5.10,
tagged 5.10 in the repository, is the last version 5 release.

Version 6.0 fixes every item listed under "Known Items Left As Is" in
CODE_REVIEW_FIXES.txt. Those items were held back in 5.10 because they
needed a new interface or would change existing files. Every change in
the source is marked with a "10/24/2026" comment. The changes were made
by Claude Code, Anthropic's AI coding assistant (https://claude.ai/code).

The library version in version.txt is 6.0 and the shared library
version (SHAREDLIB_VERSION in env/linux.env) is 6.0, so the Linux
shared library is built as libdatareel.so.6.0. Programs must be
recompiled against the 6.0 headers. Some classes gained members and
some function signatures changed (see Upgrading From Version 5).

CONTENTS
--------
How the Changes Were Tested
Linked Lists
Configuration Manager
Thread Pool
Database File Cache
Relational Database
XML Parser
RSS Feed Writer
Thread Helpers and glibc
Compiler Warnings
Other Fixes
Windows and Visual Studio 2026
Linux OpenSSL Default
Upgrading From Version 5

HOW THE CHANGES WERE TESTED
---------------------------
- The library and the OpenSSL library build with no warnings using
  GCC 13 with -Wall -Wextra, at -O0 and at -O2.
- The 39 database, general and thread example programs were built with
  AddressSanitizer and UndefinedBehaviorSanitizer and run with no
  reports.
- New test programs for each change pass against 6.0. The RDBMS, XML
  and thread pool tests fail against 5.10, which shows that they test
  the problems that were fixed. The glibc mutex problem was reproduced
  with a small C program: after a trylock that returns
  ENOTRECOVERABLE, an unlock crashes and a lock never returns.

LINKED LISTS
------------
- New gxList::DestroyArrayList() and gxListB::DestroyArrayList() free
  each item with delete[], for strings and other data allocated with
  new[]. DestroyList() still uses a scalar delete and is only for
  data allocated with a single object new.
- examples_general/doubly_linked_list uses DestroyArrayList().

CONFIGURATION MANAGER
---------------------
- gxConfig::Add(const gxString &) hid the virtual gxListB::Add(void *)
  (-Woverloaded-virtual). gxConfig now overrides Add(void *) as a
  private function that adds nothing and returns a null value, since
  every node in a configuration list must hold a gxString.
- New gxConfig::Add(const char *), so Add(char_buffer) stores the text
  as a string instead of picking the void pointer overload.

THREAD POOL
-----------
- thrPool::ClearPool() and the thrPool destructor used to delete every
  thread with a plain delete, even while it was still running. They
  now cancel running, suspended and waiting joinable threads and, under
  POSIX, wait for them to finish before deleting them.
- gxThread::DestroyThreadPool() and gxThread::DestroyThread() with
  check_state also wait for a canceled thread under POSIX.
  pthread_cancel() only sends a cancel request, so a canceled thread
  could still be running when its gxThread_t was deleted.
- New gxThreadAPIWrapper::apiStopThread() cancels a thread and waits
  for it.
- A canceled thread stops at its next cancellation point. A thread that
  never reaches one keeps these functions waiting. A canceled thread
  runs gxThread::ThreadCleanupHandler(), so the gxThread object must
  still exist when the pool is cleared. Detached threads cannot be
  canceled and are still deleted without being stopped.

DATABASE FILE CACHE
-------------------
- gxDBCacheNode::Bind() loaded a segment that was not in the cache
  with a read length of one byte. The cache node now remembers the
  length of its last CachedRead(), CachedWrite() or Bind(bytes) call
  and loads the whole node. With no length set, only a segment that is
  already cached is bound, and the node address is kept.
- New gxDBCacheNode::Bind(bytes), GetNodeLength() and SetNodeLength().
- gxDatabaseCache::ReleaseSegments() now returns the segment array and
  leaves the cache empty. It used to set only the array pointer to
  null, so the head and tail still pointed into the released array.
  The caller frees the returned array with delete[].

RELATIONAL DATABASE
-------------------
- ALLOWDUP lookups by key: with duplicates allowed the block address
  is part of each B-tree key, so a search key with a block address of
  0 never matched. FindRecord(), DeleteRecord() and the change
  functions could not find any record through an index. Find() now
  starts at the first key with the same CRC-32 value and reads each
  record with that value until one matches.
- AddRecord() no longer checks for an existing key when duplicates are
  allowed. The check always passed before, because the lookup always
  failed.
- BLOB fields in all-field keys: CreateKey() moved its write offset
  past a BLOB field but did not count it in the key length, so the
  fields after a BLOB were left out of the key. BLOB fields are now
  skipped without leaving a gap.
- File change: the all-field keys of tables with BLOB fields are
  different in 6.0. Rebuild the index files of those tables with
  RebuildIndex after upgrading. Index files of other tables do not
  change.

XML PARSER
----------
- Text mixed with child elements is kept in document order. It used
  to be dropped. Inside a parent, child or sub-element, each piece of
  text is stored as a node with an empty name and the text in its data
  member. Text between the parent elements of the root is stored in
  node_tree with the new node type gxsXML_TEXT_NODE and passed to
  Handle_Parent_Event() in SAX mode.
- Leading and trailing white space is removed from the text, and text
  that is only white space is not kept, so a document written and
  loaded again gives the same tree.
- The writer writes a node with no name as text.
- Code that walks a loaded tree will see the text nodes in documents
  with mixed content, and should skip nodes with an empty name if it
  only wants elements.

RSS FEED WRITER
---------------
- New gxRSScloud structure with domain, port, path,
  registerprocedure and protocol members. gxRSSchannel::cloud is now a
  gxRSScloud (it was a gxString).
- The <cloud> element is written as an empty element with the five
  attributes the RSS 2.0 specification requires, when cloud.domain is
  set. It used to be written as element text, which is not valid RSS.

THREAD HELPERS AND GLIBC
------------------------
- In glibc 2.39 a pthread_mutex_trylock() call that returns
  ENOTRECOVERABLE on a robust mutex leaves the mutex in a bad state: a
  later unlock crashes and a later lock never returns.
- gxMutex_t has a new not_recoverable member. It is set when
  gxThreadMutexLock() or gxThreadMutexTryLock() gets ENOTRECOVERABLE.
  The lock, try lock and unlock functions then return ENOTRECOVERABLE
  and store gxMUTEX_NOTRECOVERABLE_ERROR without calling glibc, and
  gxThreadMutexDestroy() skips its unlock call. The mutex can be
  destroyed and initialized again.

COMPILER WARNINGS
-----------------
With -Wall -Wextra the 5.10 library reported warnings at 117 places
in the source. All are fixed:
- The private copy constructors and assignment operators used to
  disallow copying no longer name their unused parameter.
  The parameters of other functions that are not used are commented
  out, or marked with (void) where only WIN32 builds use them.
- "operator const int () const" is now "operator int () const", and
  gxDatabase::GetRevLetter() returns char.
- Casts to "const unsigned char" in htmldrv.cpp and ostrbase.cpp
  are now casts to unsigned char.
- HyperText::TableHeader() and TableData() take a const char *valign,
  and PostScriptDrv::SetDocumentName() keeps a const char pointer, so
  string literals can be passed without a cast.
- Signed and unsigned compares in gxdatagm.cpp, gxstream.cpp,
  scomserv.cpp and gxrdbhdr.cpp.
- gxBStree::Find() had an unused variable.

OTHER FIXES
-----------
Found while testing the changes above:
- DiskFileB::df_copy() stored the file lengths in unsigned variables,
  so the tests for a futils_filelength() error could never be true.
- gxDatabase::ReadObjectChecksum() compared CRC values that were never
  set when the file was not open. It now returns 0. The optional CRC
  pointers are checked for null on every path.
- ASPrint() for int, long and double values formatted the number into
  a buffer the size of the field width, which overflowed for a narrow
  field. long values were truncated to int. A fixed buffer and
  snprintf() are used now.
- operator>>() for UString and UStringw read an uninitialized buffer
  when the input failed, for example at the end of the input. Found
  by AddressSanitizer in examples_general/string_class.
- MemoryBuffer::Clear(1) only set the length to zero, and Alloc(),
  Realloc(), Destroy() and the destructor freed memory without clearing
  it, so passwords and keys stayed in freed memory. Clear(1) and every
  free now overwrite the whole allocated buffer with zeros through the
  new MemoryBuffer::SecureZero(). Found in the fcrypt security review.

WINDOWS AND VISUAL STUDIO 2026
------------------------------
The nmake build files and the Windows code were reviewed and updated
for Visual Studio 2026 (MSVC 14.50). VS 2022 is still supported.

Build files:
- winslib/msvc.env used the cdebug, cflags, cvarsmt, guilflags and
  guilibsmt macros from win32.mak, which is no longer part of the
  Windows SDK. Those macros were empty, so the static library and
  every program linked to it were built with no optimization in
  release builds and no debug information in debug builds. The flags
  are now set in msvc.env: /MT /O2 for release builds and /MTd /Z7 /Od
  for debug builds, with /W3 /EHsc /GR for both.
- The Windows system libraries are now listed in msvc.env. The OpenSSL
  builds also link ws2_32.lib, which the OpenSSL 3.x static libraries
  need.
- env/msvc.mak and env/msvc_dll.mak passed LINK_FLAGS, which was never
  set, to the linker. They now pass LINKER_FLAGS. winslib/project.mak
  passes /nologo to lib.exe.
- dll/msvc.env linked with /machine:I386, so the DLL could not be
  built with the x64 compiler. The linker now takes the target CPU
  from the object files. The /pdbtype:sept option, which the linker no
  longer supports, was removed.
- _CRT_SECURE_NO_WARNINGS, _CRT_NONSTDC_NO_WARNINGS and
  _WINSOCK_DEPRECATED_NO_WARNINGS are defined in MSVC_DEFMACS, so the
  C4996 deprecation warnings for the C string, POSIX and WinSock 1.1
  functions do not hide other warnings. Comment out MSVC_DEFMACS to
  see them. _HAS_STD_BYTE=0 is also defined, so std::byte cannot
  conflict with the Windows byte type if the code is built with
  /std:c++17 or later.

ENV BAT files:
- New env/vs2026_vars.bat finds the VS 2026 install with vswhere.exe
  and calls vcvarsall.bat. It works with the Community, Professional,
  Enterprise, Build Tools and Insiders editions. The other BAT files
  called the Insiders path directly, which does not exist in a release
  install of VS 2026.
- The default Windows build is now 64-bit x64. env/msvc.bat and
  env/msvc64_vs2026.bat set up x64 builds, and env/msvc_vs2022.bat
  sets up x64 builds with VS 2022. env/msvc64_vs2026.bat called
  vcvarsamd64_x86.bat before, which builds 32-bit programs on a 64-bit
  host. For 32-bit x86 builds of legacy code use
  env/msvc32_vs2026.bat.
- The OpenSSL ENV files point to the openssl64 install by default.

Source:
- gxdtyp64.h used the old _int64 keyword. It now uses __int64.
- gxRandom::entropy_number() in gxssl.cpp and the WIN32 thread entry
  in thrapiw.cpp cast pointers to unsigned long and DWORD, which are
  32 bits in 64-bit Windows builds. They cast through size_t now. The
  detach, priority, stack and thread_pool thread examples had the same
  cast.
- The futils directory functions and futils_GetDiskSpace() use the
  ANSI Windows functions (FindFirstFileA, FindNextFileA,
  GetDiskFreeSpaceA and GetDiskFreeSpaceExA). In _UNICODE builds the
  __VS2026__ code copied a wide file name with strcpy(), which kept
  only the first letter, and passed a wide string to
  GetDiskFreeSpaceExA. futils_readdir() did not set d_namlen in ANSI
  builds.
- futils_filelength() called CreateFile(TEXT(fname)), which does not
  compile with _UNICODE when the 64-bit integer option is used. It
  calls CreateFileA() now.
- futils_mode() discarded the result of tolower(), so files ending in
  .EXE or .COM in upper case were not marked as programs.
- gxdlcode.h defines __VS2026__ when _MSC_VER is 1950 (VS 2026) or
  higher, so the VS 2026 _UNICODE code is used without a -D__VS2026__
  flag.

How these changes were tested:
- The library, the DLL, the utils and the examples were cross-compiled
  on Linux with the MinGW-w64 GCC 13 compilers for 32-bit and 64-bit
  Windows, with the MSVC defines from msvc.env, in ANSI and _UNICODE
  builds. All 64 programs link. The OpenSSL examples were not built.
- The 64-bit programs were run under Wine 9. The database, general and
  thread examples ran, the directory functions were checked in ANSI
  and _UNICODE builds, and the simple socket client and server
  exchanged data.
- The library was not built with the Microsoft compiler or nmake for
  this release. Build it with VS 2026 on Windows before you use it.

ONE LIBRARY WITH SSL
--------------------
- SSL is built into the library on every platform. There is no
  separate SSL library.
- Linux: "make" in the build directory builds libdatareel with
  OpenSSL. env/linux.env sets USE_OPEN_SSL to YES unless it is already
  set, and the example programs link with -lssl -lcrypto. The
  libdatareel_ssl library and build/makefile_ssl were removed. On a
  system without OpenSSL, build libdatareel and your programs with
  "make USE_OPEN_SSL=NO".
- Windows: the gxcode library uses the native Windows Schannel (SSPI)
  TLS API by default, so OpenSSL does not need to be built. Programs
  link with secur32.lib, crypt32.lib and ncrypt.lib, which come with
  the Windows SDK. msvc.env adds -D__USE_GX_SSL_EXTENSIONS__ and
  -D__USE_GX_SCHANNEL__. OpenSSL is still an option: build the
  library with msvc_ssl.mak and your programs with USESSL=1.

WINDOWS SCHANNEL SSL
--------------------
- New file src/gxsschan.cpp has the Schannel version of the gxSSL
  class. gxssl.cpp includes it when __USE_GX_SCHANNEL__ is defined.
  The gxSSL functions, error codes and messages are the same for both
  versions.
- Client side: TLS 1.2 and TLS 1.3 (where Windows supports it), SNI,
  and VerifyCert() with the Windows certificate chain engine. With
  SetCAList() the chain must end at a CA in the CA file. Without it the
  Windows trusted root store is used.
- Server side: InitCTX() loads PEM or DER cert and key files. RSA
  (PKCS#1 and PKCS#8) and EC keys are supported, and the key is
  imported into CNG for the life of the context. A .pfx or .p12 file
  with the key password also works. Encrypted PEM keys need a .pfx
  file.
- GetCTX() and GetSSL() return Schannel objects, so programs that call
  OpenSSL functions directly must use the OpenSSL build.
- MakeDHParms(), MakeRSAPrivateKey() and MakeCertReq() need OpenSSL and
  return an error in the Schannel build. LoadDHParms(), InitPRNG() and
  MakeRSAEphKey() do nothing because Schannel handles key exchange.
- New gxSSL functions for both versions: SetHostName() for SNI,
  GetPeerCertInfo() for the peer's subject and issuer, and
  CertVerifyErrorString() for VerifyCert() return codes.
  gxsHTTPClient sets the host name, so HTTPS requests to virtual hosted
  sites work with both versions.
- The ssl_verify_cert example uses the new functions and works with
  both versions. The https_client, http_client, ssl_server and
  ssl_verify_cert msvc.mak files no longer set USESSL=1.
- Tested by cross-compiling with MinGW-w64 for x86 and x64 and running
  under Wine 9: the client against openssl s_server, cert checks with
  a good and a bad host name, the server with a .pfx file against a
  Python ssl client, a 1 MB transfer, a read timeout, and the
  https_client example. Wine cannot import PEM keys into CNG, so the
  PEM server path and TLS 1.3 still need a test on Windows.
- Tested on Windows 11 (10.0.26200) with VS 2026 (cl 19.50) x64:
  the library, the DLL and all 61 examples and utils build with nmake.
  Cert verify, https_client and the ssl_server example (PEM key) pass
  with TLS 1.2 and TLS 1.3.
- MakeCertReq() signed certs with MD5, which Schannel rejects. It
  signs with SHA-256 now, and the ssl_server example cert was
  re-signed with SHA-256.
- The debug DLL link no longer uses /incremental:yes (LNK4224).

UPGRADING FROM VERSION 5
------------------------
- Recompile every program against the 6.0 headers and link with the
  6.0 library.
- gxRSSchannel::cloud is a gxRSScloud. Set cloud.domain, cloud.port,
  cloud.path, cloud.registerprocedure and cloud.protocol instead of
  assigning a string.
- gxDatabaseCache::ReleaseSegments() returns the segment array. Free it
  with delete[] when it is no longer used.
- gxConfig objects no longer accept untyped data through
  gxListB::Add(void *).
- Rebuild the index files of RDBMS tables that have BLOB fields and an
  all-field key ("PRI: COL NONE").
- Programs that walk gxsXML trees loaded from documents with mixed
  content should skip nodes with an empty name, or handle them as text.
- A thread pool is now cleared by stopping its running joinable
  threads. Threads must reach a cancellation point, and the gxThread
  object must still exist when the pool is cleared or deleted.

DataReel Copyright (c) 2001-2026 DataReel Software Development
