Author: rinrab
Date: Wed Apr  1 16:52:16 2026
New Revision: 1932705

Log:
svnbrowse: Create new blank program to implement a TUI browser for repository
directories. i.e. interactive interface for 'svn ls'.

The program itself is an empty application with some boilerplate copied from
other command-line tools. Also perform ncurses initialization, and test that
everything works great; No real functionality is implemented yet.

* CMakeLists.txt
  (SVN_ENABLE_TUI): New option.
  (ncurses): Look for the library.
  (configsummary): Log state of SVN_ENABLE_TUI.
* build.conf
  (svnbrowse): Declare target.
* build/generator/gen_cmake.py
  (get_target_conditions): Add special handling for svnbrowse target so it is
   enabled only if TUIs are ON.
* subversion/svnbrowse/svnbrowse.c: New file.
  (ui_draw): This function will be rendering everything to the screen.  
  (sub_main): New function, a wrapper around main() with pools and svn-errors.
  (main): New main.

Please follow the discussion on [email protected]. for more info.

[1] https://lists.apache.org/thread/knc20xgb6h56jl0tvxbywhgt5yvh90vg

Added:
   subversion/trunk/subversion/svnbrowse/
   subversion/trunk/subversion/svnbrowse/svnbrowse.c   (contents, props changed)
Modified:
   subversion/trunk/CMakeLists.txt
   subversion/trunk/build.conf
   subversion/trunk/build/generator/gen_cmake.py

Modified: subversion/trunk/CMakeLists.txt
==============================================================================
--- subversion/trunk/CMakeLists.txt     Wed Apr  1 16:20:29 2026        
(r1932704)
+++ subversion/trunk/CMakeLists.txt     Wed Apr  1 16:52:16 2026        
(r1932705)
@@ -88,6 +88,7 @@ option(SVN_ENABLE_FS_FS "Enable Subversi
 option(SVN_ENABLE_FS_X "Enable Subversion FSX Repository Filesystem Library" 
ON)
 option(SVN_ENABLE_FS_BASE "Enable Subversion Filesystem Base Library (NOT 
IMPLEMENTED and DEPRECATED)" OFF)
 option(SVN_ENABLE_NLS "Enable gettext functionality" OFF)
+option(SVN_ENABLE_TUI "Enable TUI applications (requires ncurses)" OFF)
 option(SVN_ENABLE_AUTH_KWALLET "Enable KWallet auth library" OFF)
 option(SVN_ENABLE_AUTH_GNOME_KEYRING "Enable GNOME Keyring for auth 
credentials" OFF)
 cmake_dependent_option(SVN_ENABLE_AUTH_GPG_AGENT "Enable GPG Agent support" 
OFF "WIN32" ON)
@@ -483,6 +484,17 @@ if(SVN_ENABLE_AUTH_GNOME_KEYRING)
   )
 endif()
 
+if (SVN_ENABLE_TUI)
+  add_library(external-ncurses INTERFACE)
+
+  if(SVN_USE_PKG_CONFIG)
+    pkg_check_modules(ncurses REQUIRED IMPORTED_TARGET ncurses)
+    target_link_libraries(external-ncurses INTERFACE PkgConfig::ncurses)
+  else()
+    message(ERROR "not supported")
+  endif()
+endif()
+
 if(SVN_ENABLE_SWIG_PERL OR SVN_ENABLE_SWIG_PYTHON OR SVN_ENABLE_SWIG_RUBY)
   find_package(SWIG REQUIRED)
   include(${SWIG_USE_FILE})
@@ -996,6 +1008,7 @@ message(STATUS "  Build type ...........
 message(STATUS "    Build shared libraries ........ : ${BUILD_SHARED_LIBS}")
 message(STATUS "    Build shared FS Modues ........ : ${SVN_BUILD_SHARED_FS}")
 message(STATUS "    Build shared RA Modues ........ : ${SVN_BUILD_SHARED_RA}")
+message(STATUS "    Build TUI applications ........ : ${SVN_ENABLE_TUI}")
 message(STATUS "    Use pkg-config dependencies ... : ${SVN_USE_PKG_CONFIG}")
 message(STATUS "    Checksum Backend .............. : ${SVN_CHECKSUM_BACKEND}")
 message(STATUS "  FS modules:")

Modified: subversion/trunk/build.conf
==============================================================================
--- subversion/trunk/build.conf Wed Apr  1 16:20:29 2026        (r1932704)
+++ subversion/trunk/build.conf Wed Apr  1 16:52:16 2026        (r1932705)
@@ -222,6 +222,12 @@ libs = libsvn_client libsvn_ra libsvn_su
 install = bin
 manpages = subversion/svnmucc/svnmucc.1
 
+[svnbrowse]
+type = exe
+path = subversion/svnbrowse
+libs = libsvn_client libsvn_ra libsvn_subr libsvn_delta ncurses
+install = bin
+
 # Support for GNOME Keyring
 [libsvn_auth_gnome_keyring]
 description = Subversion GNOME Keyring Library
@@ -1505,6 +1511,10 @@ external-lib = $(SVN_SASL_LIBS)
 type = lib
 external-lib = $(SVN_OPENSSL_LIBS) $(SVN_LIBCRYPTO_LIBS)
 
+[ncurses]
+type = lib
+external-lib = $(SVN_NCURSES_LIBS)
+
 [checksum-libs]
 type = lib
 external-lib = $(SVN_CHECKSUM_LIBS)

Modified: subversion/trunk/build/generator/gen_cmake.py
==============================================================================
--- subversion/trunk/build/generator/gen_cmake.py       Wed Apr  1 16:20:29 
2026        (r1932704)
+++ subversion/trunk/build/generator/gen_cmake.py       Wed Apr  1 16:52:16 
2026        (r1932705)
@@ -79,6 +79,8 @@ def get_target_conditions(target):
       enable_condition.append("SVN_ENABLE_AUTH_KWALLET")
     elif target.name == "libsvn_auth_gnome_keyring":
       enable_condition.append("SVN_ENABLE_AUTH_GNOME_KEYRING")
+    elif target.name == "svnbrowse":
+      enable_condition.append("SVN_ENABLE_TUI")
 
   if isinstance(target, gen_base.TargetExe):
     if target.install == "test" or target.install == "sub-test":

Added: subversion/trunk/subversion/svnbrowse/svnbrowse.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.c   Wed Apr  1 16:52:16 
2026        (r1932705)
@@ -0,0 +1,108 @@
+/*
+ * svnbrowse.c:  TUI (terminal user interface) client for browsing and
+ *               exploring remote Subversion targets.
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <apr.h>
+
+#include "svn_pools.h"
+#include "svn_cmdline.h"
+#include "svn_error.h"
+
+#include <ncurses.h>
+
+/* Control+ASCII character are represented as values 1-26 according to their
+ * alphabetical order. */
+#define CTRL(ch) (ch - 'a' + 1)
+
+static void
+ui_draw(apr_pool_t *pool)
+{
+  mvprintw(0, 4, "svnbrowse: work in progress");
+}
+
+static svn_error_t *
+sub_main(int *code, int argc, char *argv[], apr_pool_t *pool)
+{
+  /* init the display */
+  initscr();
+
+  /* put ncurses into keypad mode to handle arrow inputs */
+  intrflush(stdscr, FALSE);
+  keypad(stdscr, TRUE);
+  nonl();
+
+  while (TRUE)
+    {
+      int ch;
+
+      clear();
+      ui_draw(pool);
+      refresh();
+
+      ch = getch();
+
+      /* getch() reads the next character/key with the following additional
+       * rules:
+       * 1. as we configured it to use keypad(), arrows and other special keys
+       *    are handled as KEY_XXX.
+       * 2. Control (CTRL) version are handled as literal 1-26 values of ch 
where
+       *    1 is <C-A> and 26 is <C-Z>.
+       * 3. The rest of keys remain as their equivalents on the current layout.
+       * 4. If shift is held, they just become uppercased.
+       */
+
+      /* TODO: quit via escape. some say just check for 27, but it I think it's
+       * a bit ugly. */
+      if (ch == 'q')
+        {
+          break;
+        }
+    }
+
+       endwin();
+
+  return SVN_NO_ERROR;
+}
+
+int main(int argc, char *argv[])
+{
+  apr_pool_t *pool;
+  int exit_code = EXIT_SUCCESS;
+  svn_error_t *err;
+
+  if (svn_cmdline_init("svnbrowse", stderr) != EXIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+  err = sub_main(&exit_code, argc, argv, pool);
+
+  if (err)
+    {
+      exit_code = EXIT_FAILURE;
+      svn_cmdline_handle_exit_error(err, NULL, "svnbrowse: ");
+    }
+
+  svn_pool_destroy(pool);
+  return exit_code;
+}

Reply via email to