[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] pager support for command line client

From: Stefan Sperling <stsp_at_elego.de>
Date: Mon, 3 Feb 2014 21:02:50 +0100

I would like to discuss the idea of using a pager by default
for some commands.

This has been discussed before, e.g. here:
http://svn.haxx.se/dev/archive-2004-09/0132.shtml
and here: http://svn.haxx.se/users/archive-2009-08/0602.shtml

Both discussions were inconclusive. Generally people seem to
feel that this is a good idea, but how exactly it should be
configured is an open question.

Some other tools, such as git, have started to create some
additional mind share in this idea.

Below is a very simple proof of concept patch that enables a
pager for 'help', 'diff', and 'blame'. Currently it only looks
for a PAGER or SVN_PAGER environment variable. My plan is to
also add a 'pager-cmd' option for this purpose.

I don't expect to commit this in its current state.
I'd rather discuss the details beforehand.

I'd appreciate a test compile on windows :)

Thoughts?

(Sorry, no log message because I'm on a train that's about to arrive...)

Index: subversion/svn/blame-cmd.c
===================================================================
--- subversion/svn/blame-cmd.c (revision 1563984)
+++ subversion/svn/blame-cmd.c (working copy)
@@ -246,6 +246,7 @@ svn_cl__blame(apr_getopt_t *os,
   svn_boolean_t end_revision_unspecified = FALSE;
   svn_diff_file_options_t *diff_options = svn_diff_file_options_create(pool);
   svn_boolean_t seen_nonexistent_target = FALSE;
+ apr_proc_t *pager_proc = NULL;
 
   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
                                                       opt_state->targets,
@@ -316,6 +317,9 @@ svn_cl__blame(apr_getopt_t *os,
                                   "mode"));
     }
 
+ if (!opt_state->non_interactive && !opt_state->xml)
+ SVN_ERR(svn_cl__start_pager(&pager_proc, pool, pool));
+
   for (i = 0; i < targets->nelts; i++)
     {
       svn_error_t *err;
@@ -409,6 +413,9 @@ svn_cl__blame(apr_getopt_t *os,
   if (opt_state->xml && ! opt_state->incremental)
     SVN_ERR(svn_cl__xml_print_footer("blame", pool));
 
+ if (pager_proc)
+ SVN_ERR(svn_cl__close_pager(pager_proc, pool));
+
   if (seen_nonexistent_target)
     return svn_error_create(
       SVN_ERR_ILLEGAL_TARGET, NULL,
Index: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (revision 1563984)
+++ subversion/svn/cl.h (working copy)
@@ -848,6 +848,22 @@ svn_cl__deprecated_merge_reintegrate(const char *s
                                      svn_client_ctx_t *ctx,
                                      apr_pool_t *pool);
 
+
+/* Launch an external pager program and redirect stdout to the pager.
+ *
+ * If the pager was started, return a pointer to the process handle
+ * for the pager in *PAGER_PROC. Else, set *PAGER_PROC to NULL. */
+svn_error_t *
+svn_cl__start_pager(apr_proc_t **pager_proc,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Close the pager with process handle PAGER_PROC.
+ * Must be called after all output has been written. */
+svn_error_t *
+svn_cl__close_pager(apr_proc_t *pager_proc,
+ apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: subversion/svn/diff-cmd.c
===================================================================
--- subversion/svn/diff-cmd.c (revision 1563984)
+++ subversion/svn/diff-cmd.c (working copy)
@@ -188,6 +188,7 @@ svn_cl__diff(apr_getopt_t *os,
   struct summarize_baton_t summarize_baton;
   const svn_client_diff_summarize_func_t summarize_func =
     (opt_state->xml ? summarize_xml : summarize_regular);
+ apr_proc_t *pager_proc = NULL;
 
   if (opt_state->extensions)
     options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
@@ -355,6 +356,9 @@ svn_cl__diff(apr_getopt_t *os,
 
   svn_opt_push_implicit_dot_target(targets, pool);
 
+ if (!opt_state->non_interactive && !opt_state->diff.summarize)
+ SVN_ERR(svn_cl__start_pager(&pager_proc, pool, pool));
+
   iterpool = svn_pool_create(pool);
 
   for (i = 0; i < targets->nelts; ++i)
@@ -488,5 +492,8 @@ svn_cl__diff(apr_getopt_t *os,
 
   svn_pool_destroy(iterpool);
 
+ if (pager_proc)
+ SVN_ERR(svn_cl__close_pager(pager_proc, pool));
+
   return SVN_NO_ERROR;
 }
Index: subversion/svn/help-cmd.c
===================================================================
--- subversion/svn/help-cmd.c (revision 1563984)
+++ subversion/svn/help-cmd.c (working copy)
@@ -45,6 +45,7 @@ svn_cl__help(apr_getopt_t *os,
 {
   svn_cl__opt_state_t *opt_state = NULL;
   svn_stringbuf_t *version_footer = NULL;
+ apr_proc_t *pager_proc = NULL;
 
   char help_header[] =
   N_("usage: svn <subcommand> [options] [args]\n"
@@ -132,16 +133,24 @@ svn_cl__help(apr_getopt_t *os,
     version_footer = svn_stringbuf_create(ra_desc_start, pool);
   SVN_ERR(svn_ra_print_modules(version_footer, pool));
 
- return svn_opt_print_help4(os,
- "svn", /* ### erm, derive somehow? */
- opt_state ? opt_state->version : FALSE,
- opt_state ? opt_state->quiet : FALSE,
- opt_state ? opt_state->verbose : FALSE,
- version_footer->data,
- help_header, /* already gettext()'d */
- svn_cl__cmd_table,
- svn_cl__options,
- svn_cl__global_options,
- _(help_footer),
- pool);
+ if (!opt_state->non_interactive)
+ SVN_ERR(svn_cl__start_pager(&pager_proc, pool, pool));
+
+ SVN_ERR(svn_opt_print_help4(os,
+ "svn", /* ### erm, derive somehow? */
+ opt_state ? opt_state->version : FALSE,
+ opt_state ? opt_state->quiet : FALSE,
+ opt_state ? opt_state->verbose : FALSE,
+ version_footer->data,
+ help_header, /* already gettext()'d */
+ svn_cl__cmd_table,
+ svn_cl__options,
+ svn_cl__global_options,
+ _(help_footer),
+ pool));
+
+ if (pager_proc)
+ SVN_ERR(svn_cl__close_pager(pager_proc, pool));
+
+ return SVN_NO_ERROR;
 }
Index: subversion/svn/util.c
===================================================================
--- subversion/svn/util.c (revision 1563984)
+++ subversion/svn/util.c (working copy)
@@ -33,6 +33,15 @@
 #include <ctype.h>
 #include <assert.h>
 
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <crtdbg.h>
+#include <io.h>
+#include <conio.h>
+#endif
+
+#include <apr.h> /* for STDOUT_FILENO */
 #include <apr_env.h>
 #include <apr_errno.h>
 #include <apr_file_info.h>
@@ -1062,3 +1071,86 @@ svn_cl__propset_print_binary_mime_type_warning(apr
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_cl__start_pager(apr_proc_t **pager_proc,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *pager_cmd;
+ const char *pager;
+ const char **args;
+ apr_file_t *stdout_file;
+ apr_status_t apr_err;
+ int i;
+
+ *pager_proc = NULL;
+
+ pager = getenv("SVN_PAGER");
+ if (pager == NULL)
+ pager = getenv("PAGER");
+ /* TODO get pager-cmd from config */
+ if (pager == NULL)
+ return SVN_NO_ERROR;
+
+#ifdef WIN32
+ if (_isatty(STDOUT_FILENO) == 0)
+ return SVN_NO_ERROR;
+#else
+ if (isatty(STDOUT_FILENO) == 0)
+ return SVN_NO_ERROR;
+#endif
+
+ pager_cmd = svn_cstring_split(pager, " \t", TRUE, scratch_pool);
+ if (pager_cmd->nelts <= 0)
+ return SVN_NO_ERROR;
+ args = apr_pcalloc(scratch_pool,
+ (sizeof(const char *) * pager_cmd->nelts + 1));
+ for (i = 0; i < pager_cmd->nelts; i++)
+ args[i] = APR_ARRAY_IDX(pager_cmd, i, const char *);
+ args[i] = NULL;
+
+ apr_err = apr_file_open_stdout(&stdout_file, scratch_pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err, "Can't open stdout");
+
+ *pager_proc = apr_pcalloc(result_pool, sizeof(**pager_proc));
+ SVN_ERR(svn_io_start_cmd3(*pager_proc, NULL, args[0], args, NULL, TRUE,
+ TRUE, NULL,
+ FALSE, NULL,
+ FALSE, NULL,
+ result_pool));
+
+ apr_err = apr_file_dup2(stdout_file, (*pager_proc)->in, result_pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err, "Can't redirect stdout");
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__close_pager(apr_proc_t *pager_proc,
+ apr_pool_t *scratch_pool)
+{
+ apr_file_t *stdout_file;
+ apr_status_t apr_err;
+ svn_error_t *err;
+
+ apr_err = apr_file_open_stdout(&stdout_file, scratch_pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err, "Can't open stdout");
+ SVN_ERR(svn_io_file_close(stdout_file, scratch_pool));
+ SVN_ERR(svn_io_file_close(pager_proc->in, scratch_pool));
+ err = svn_io_wait_for_cmd(pager_proc,
+ apr_psprintf(scratch_pool, "%ld",
+ (long)pager_proc->pid),
+ NULL, NULL, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_EXTERNAL_PROGRAM)
+ {
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ }
+ else
+ SVN_ERR(err);
+
+ return SVN_NO_ERROR;
+}
Received on 2014-02-03 22:59:33 CET

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.