Author: rinrab
Date: Wed Apr 1 17:44:08 2026
New Revision: 1932708
Log:
svnbrowse: Implement the most basic functionality of the directory browser.
- Simple command-line parser that takes the first argument processes it as the
URL to operate on.
- Initialize the client context with pre-set authentication handlers. Use the
standard command line handlers for that.
- Retrieving the directory contents and display them.
- Allow some control to navigate to subdirs.
* subversion/svnbrowse/svnbrowse.c
(includes): Include required headers.
(item_t): Declare struct to hold information about a single directory.
(svn_browse__ctx_t): Declare struct to hold context of the browser.
(init_client, list_cb, enter_path): New functions.
(ui_draw): Render the list.
(sub_main): Add initialization and handle browser commands.
Modified:
subversion/trunk/subversion/svnbrowse/svnbrowse.c
Modified: subversion/trunk/subversion/svnbrowse/svnbrowse.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/svnbrowse.c Wed Apr 1 17:21:18 2026 (r1932707)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.c Wed Apr 1 17:44:08 2026 (r1932708)
@@ -24,6 +24,9 @@
#include <apr.h>
+#include "svn_client.h"
+#include "svn_opt.h"
+#include "svn_path.h"
#include "svn_pools.h"
#include "svn_cmdline.h"
#include "svn_error.h"
@@ -34,15 +37,124 @@
* alphabetical order. */
#define CTRL(ch) (ch - 'a' + 1)
+typedef struct item_t {
+ const char *relpath;
+ const svn_dirent_t *dirent;
+} item_t;
+
+typedef struct svn_browse__ctx_t {
+ const char *root;
+ const char *relpath;
+ const char *abspath;
+ svn_opt_revision_t revision;
+
+ svn_client_ctx_t *client;
+
+ apr_array_header_t *list;
+ int selection;
+ apr_pool_t *list_pool;
+} svn_browse__ctx_t;
+
+static svn_error_t *
+init_client(svn_browse__ctx_t *ctx, apr_pool_t *pool)
+{
+ svn_auth_baton_t *auth;
+
+ SVN_ERR(svn_client_create_context2(&ctx->client, NULL, pool));
+
+ /* Set up Authentication stuff. */
+ SVN_ERR(svn_cmdline_create_auth_baton2(&auth, FALSE, NULL, NULL, NULL, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, NULL, NULL, pool));
+
+ ctx->client->auth_baton = auth;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+list_cb(void *baton,
+ const char *path,
+ const svn_dirent_t *dirent,
+ const svn_lock_t *lock,
+ const char *abs_path,
+ const char *external_parent_url,
+ const char *external_target,
+ apr_pool_t *scratch_pool)
+{
+ svn_browse__ctx_t *ctx = baton;
+ item_t *item = apr_pcalloc(ctx->list_pool, sizeof(*item));
+ item->relpath = apr_pstrdup(ctx->list_pool, path);
+ item->dirent = svn_dirent_dup(dirent, ctx->list_pool);
+ APR_ARRAY_PUSH(ctx->list, item_t *) = item;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+enter_path(svn_browse__ctx_t *ctx, const char *relpath, apr_pool_t *pool)
+{
+ ctx->relpath = apr_pstrdup(pool, relpath);
+ ctx->abspath = svn_path_url_add_component2(ctx->root, relpath, pool);
+
+ ctx->list = apr_array_make(pool, 0, sizeof(item_t *));
+ ctx->selection = 0;
+
+ SVN_ERR(svn_client_list4(ctx->abspath, &ctx->revision, &ctx->revision, NULL,
+ svn_depth_immediates, SVN_DIRENT_ALL, TRUE, TRUE,
+ list_cb, ctx, ctx->client, pool));
+
+ return SVN_NO_ERROR;
+}
+
static void
-ui_draw(apr_pool_t *pool)
+ui_draw(svn_browse__ctx_t *ctx, apr_pool_t *pool)
{
- mvprintw(0, 4, "svnbrowse: work in progress");
+ int i;
+
+ mvprintw(0, 4, "Browsing: %s", ctx->abspath);
+
+ for (i = 0; i < ctx->list->nelts; i++)
+ {
+ item_t *item = APR_ARRAY_IDX(ctx->list, i, item_t *);
+
+ if (i == ctx->selection)
+ standout();
+
+ if (i == 0)
+ mvprintw(i + 1, 0, "../");
+ else if (item->dirent->kind == svn_node_dir)
+ mvprintw(i + 1, 0, "%s/", item->relpath);
+ else if (item->dirent->kind == svn_node_file)
+ mvprintw(i + 1, 0, "%s", item->relpath);
+ else
+ abort();
+
+ mvprintw(i + 1, COLS - 40, "%8ld KiB r%-8ld %s",
+ item->dirent->size / 1024,
+ item->dirent->created_rev,
+ item->dirent->last_author);
+
+ if (i == ctx->selection)
+ standend();
+ }
}
static svn_error_t *
sub_main(int *code, int argc, char *argv[], apr_pool_t *pool)
{
+ svn_browse__ctx_t ctx = { 0 };
+
+ if (argc != 2)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ "usage: svnbrowse <URL>");
+
+ SVN_ERR(svn_uri_canonicalize_safe(&ctx.root, NULL, argv[1], pool, pool));
+ ctx.revision.kind = svn_opt_revision_head;
+ ctx.list_pool = pool;
+
+ SVN_ERR(init_client(&ctx, pool));
+ SVN_ERR(enter_path(&ctx, "", pool));
+
/* init the display */
initscr();
@@ -56,7 +168,7 @@ sub_main(int *code, int argc, char *argv
int ch;
clear();
- ui_draw(pool);
+ ui_draw(&ctx, pool);
refresh();
ch = getch();
@@ -71,9 +183,28 @@ sub_main(int *code, int argc, char *argv
* 4. If shift is held, they just become uppercased.
*/
+ if (ch == KEY_UP || ch == 'k')
+ {
+ ctx.selection--;
+ }
+ else if (ch == KEY_DOWN || ch == 'j')
+ {
+ ctx.selection++;
+ }
+ else if (ch == '\n' || ch == '\r')
+ {
+ item_t *item = APR_ARRAY_IDX(ctx.list, ctx.selection, item_t *);
+ const char *new_url = svn_relpath_join(ctx.relpath, item->relpath, pool);
+ SVN_ERR(enter_path(&ctx, new_url, pool));
+ }
+ else if (ch == KEY_BACKSPACE || ch == '-' || ch == 'u')
+ {
+ const char *new_url = svn_relpath_dirname(ctx.relpath, pool);
+ SVN_ERR(enter_path(&ctx, new_url, pool));
+ }
/* TODO: quit via escape. some say just check for 27, but it I think it's
* a bit ugly. */
- if (ch == 'q')
+ else if (ch == 'q')
{
break;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.