Author: julianfoad
Date: Fri Mar  9 11:06:23 2018
New Revision: 1826328

URL: http://svn.apache.org/viewvc?rev=1826328&view=rev
Log:
Allow splitting long help strings into paragraphs to ease translation.

Issue SVN-4724.

Some of the tools our translators use do not handle very long (multi-
paragraph) strings well, just showing the whole string has changed. The
recommended solution is to split long strings at paragraph boundaries in the
source code.

This patch revises svn_opt_subcommand_desc2_t into svn_opt_subcommand_desc3_t
supporting an array of strings instead of a single string for the main help
text, and revises all the supporting functions accordingly.

This patch does not split each long string into paragrpahs, it only adds the
mechanism for doing so.

* subversion/include/svn_opt.h,
  subversion/libsvn_subr/opt.c
  (SVN_OPT_MAX_PARAGRAPHS,
   svn_opt_subcommand_desc3_t,
   svn_opt_get_canonical_subcommand3,
   svn_opt_get_option_from_code3,
   svn_opt_subcommand_takes_option4,
   svn_opt_print_generic_help3,
   svn_opt_subcommand_help4,
   svn_opt_print_help5): New.

Modified:
    subversion/trunk/subversion/include/svn_opt.h
    subversion/trunk/subversion/libsvn_subr/opt.c

Modified: subversion/trunk/subversion/include/svn_opt.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_opt.h?rev=1826328&r1=1826327&r2=1826328&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_opt.h (original)
+++ subversion/trunk/subversion/include/svn_opt.h Fri Mar  9 11:06:23 2018
@@ -69,6 +69,9 @@ typedef svn_error_t *(svn_opt_subcommand
 /** The maximum number of options that can be accepted by a subcommand. */
 #define SVN_OPT_MAX_OPTIONS 50
 
+/** The maximum number of paragraphs of help text a subcommand can have. */
+#define SVN_OPT_MAX_PARAGRAPHS 50
+
 /** Options that have no short option char should use an identifying
  * integer equal to or greater than this.
  */
@@ -77,6 +80,37 @@ typedef svn_error_t *(svn_opt_subcommand
 
 /** One element of a subcommand dispatch table.
  *
+ * @since New in 1.11.
+ */
+typedef struct svn_opt_subcommand_desc3_t
+{
+  /** The full name of this command. */
+  const char *name;
+
+  /** The function this command invokes. */
+  svn_opt_subcommand_t *cmd_func;
+
+  /** A list of alias names for this command (e.g., 'up' for 'update'). */
+  const char *aliases[SVN_OPT_MAX_ALIASES];
+
+  /** A multi-paragraph string describing this command. */
+  const char *help[SVN_OPT_MAX_PARAGRAPHS];
+
+  /** A list of options accepted by this command.  Each value in the
+   * array is a unique enum (the 2nd field in apr_getopt_option_t)
+   */
+  int valid_options[SVN_OPT_MAX_OPTIONS];
+
+  /** A list of option help descriptions, keyed by the option unique enum
+   * (the 2nd field in apr_getopt_option_t), which override the generic
+   * descriptions given in an apr_getopt_option_t on a per-subcommand basis.
+   */
+  struct { int optch; const char *desc; } desc_overrides[SVN_OPT_MAX_OPTIONS];
+} svn_opt_subcommand_desc3_t;
+
+
+/** One element of a subcommand dispatch table.
+ *
  * @since New in 1.4.
  */
 typedef struct svn_opt_subcommand_desc2_t
@@ -139,6 +173,17 @@ typedef struct svn_opt_subcommand_desc_t
  * Return the entry in @a table whose name matches @a cmd_name, or @c NULL if
  * none.  @a cmd_name may be an alias.
  *
+ * @since New in 1.11.
+ */
+const svn_opt_subcommand_desc3_t *
+svn_opt_get_canonical_subcommand3(const svn_opt_subcommand_desc3_t *table,
+                                  const char *cmd_name);
+
+
+/**
+ * Same as svn_opt_get_canonical_subcommand3(), but with a different
+ * version of the subcommand description table.
+ *
  * @since New in 1.4.
  */
 const svn_opt_subcommand_desc2_t *
@@ -170,6 +215,18 @@ svn_opt_get_canonical_subcommand(const s
  *
  * The returned value may be statically allocated, or allocated in @a pool.
  *
+ * @since New in 1.11.
+ */
+const apr_getopt_option_t *
+svn_opt_get_option_from_code3(int code,
+                              const apr_getopt_option_t *option_table,
+                              const svn_opt_subcommand_desc3_t *command,
+                              apr_pool_t *pool);
+
+/**
+ * Same as svn_opt_get_option_from_code3(), but with a different
+ * version of the subcommand description table.
+ *
  * @since New in 1.4.
  */
 const apr_getopt_option_t *
@@ -198,6 +255,17 @@ svn_opt_get_option_from_code(int code,
  * non-NULL, it is a zero-terminated array, and all subcommands take
  * the options listed in it.
  *
+ * @since New in 1.11.
+ */
+svn_boolean_t
+svn_opt_subcommand_takes_option4(const svn_opt_subcommand_desc3_t *command,
+                                 int option_code,
+                                 const int *global_options);
+
+/**
+ * Same as svn_opt_subcommand_takes_option4(), but with a different
+ * version of the subcommand description table.
+ *
  * @since New in 1.5.
  */
 svn_boolean_t
@@ -244,6 +312,20 @@ svn_opt_subcommand_takes_option(const sv
  *
  * Use @a pool for temporary allocation.
  *
+ * @since New in 1.11.
+ */
+void
+svn_opt_print_generic_help3(const char *header,
+                            const svn_opt_subcommand_desc3_t *cmd_table,
+                            const apr_getopt_option_t *opt_table,
+                            const char *footer,
+                            apr_pool_t *pool,
+                            FILE *stream);
+
+/**
+ * Same as svn_opt_print_generic_help3(), but with a different
+ * version of the subcommand description table.
+ *
  * @since New in 1.4.
  */
 void
@@ -297,6 +379,19 @@ svn_opt_format_option(const char **strin
  * use that second name as an alias for the first name.  This additional
  * behaviour is new in 1.7.
  *
+ * @since New in 1.11.
+ */
+void
+svn_opt_subcommand_help4(const char *subcommand,
+                         const svn_opt_subcommand_desc3_t *table,
+                         const apr_getopt_option_t *options_table,
+                         const int *global_options,
+                         apr_pool_t *pool);
+
+/**
+ * Same as svn_opt_subcommand_help4(), but with a different
+ * version of the subcommand description table.
+ *
  * @since New in 1.5.
  */
 void
@@ -700,9 +795,28 @@ svn_opt_parse_path(svn_opt_revision_t *r
  * --version flag *and* subcommand arguments on a help command line.
  * The logic for handling such a situation should be in one place.
  *
- * @since New in 1.8.
+ * @since New in 1.11.
  */
+svn_error_t *
+svn_opt_print_help5(apr_getopt_t *os,
+                    const char *pgm_name,
+                    svn_boolean_t print_version,
+                    svn_boolean_t quiet,
+                    svn_boolean_t verbose,
+                    const char *version_footer,
+                    const char *header,
+                    const svn_opt_subcommand_desc3_t *cmd_table,
+                    const apr_getopt_option_t *option_table,
+                    const int *global_options,
+                    const char *footer,
+                    apr_pool_t *pool);
 
+/**
+ * Same as svn_opt_print_help5(), but with a different
+ * version of the subcommand description table.
+ *
+ * @since New in 1.8.
+ */
 svn_error_t *
 svn_opt_print_help4(apr_getopt_t *os,
                     const char *pgm_name,

Modified: subversion/trunk/subversion/libsvn_subr/opt.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/opt.c?rev=1826328&r1=1826327&r2=1826328&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/opt.c (original)
+++ subversion/trunk/subversion/libsvn_subr/opt.c Fri Mar  9 11:06:23 2018
@@ -55,6 +55,30 @@
 
 /*** Code. ***/
 
+const svn_opt_subcommand_desc3_t *
+svn_opt_get_canonical_subcommand3(const svn_opt_subcommand_desc3_t *table,
+                                  const char *cmd_name)
+{
+  int i = 0;
+
+  if (cmd_name == NULL)
+    return NULL;
+
+  while (table[i].name) {
+    int j;
+    if (strcmp(cmd_name, table[i].name) == 0)
+      return table + i;
+    for (j = 0; (j < SVN_OPT_MAX_ALIASES) && table[i].aliases[j]; j++)
+      if (strcmp(cmd_name, table[i].aliases[j]) == 0)
+        return table + i;
+
+    i++;
+  }
+
+  /* If we get here, there was no matching subcommand name or alias. */
+  return NULL;
+}
+
 const svn_opt_subcommand_desc2_t *
 svn_opt_get_canonical_subcommand2(const svn_opt_subcommand_desc2_t *table,
                                   const char *cmd_name)
@@ -80,6 +104,38 @@ svn_opt_get_canonical_subcommand2(const
 }
 
 const apr_getopt_option_t *
+svn_opt_get_option_from_code3(int code,
+                              const apr_getopt_option_t *option_table,
+                              const svn_opt_subcommand_desc3_t *command,
+                              apr_pool_t *pool)
+{
+  apr_size_t i;
+
+  for (i = 0; option_table[i].optch; i++)
+    if (option_table[i].optch == code)
+      {
+        if (command)
+          {
+            int j;
+
+            for (j = 0; ((j < SVN_OPT_MAX_OPTIONS) &&
+                         command->desc_overrides[j].optch); j++)
+              if (command->desc_overrides[j].optch == code)
+                {
+                  apr_getopt_option_t *tmpopt =
+                      apr_palloc(pool, sizeof(*tmpopt));
+                  *tmpopt = option_table[i];
+                  tmpopt->description = command->desc_overrides[j].desc;
+                  return tmpopt;
+                }
+          }
+        return &(option_table[i]);
+      }
+
+  return NULL;
+}
+
+const apr_getopt_option_t *
 svn_opt_get_option_from_code2(int code,
                               const apr_getopt_option_t *option_table,
                               const svn_opt_subcommand_desc2_t *command,
@@ -126,6 +182,34 @@ svn_opt_get_option_from_code(int code,
 }
 
 
+/* Like svn_opt_get_option_from_code3(), but also, if CODE appears a second
+ * time in OPTION_TABLE with a different name, then set *LONG_ALIAS to that
+ * second name, else set it to NULL. */
+static const apr_getopt_option_t *
+get_option_from_code3(const char **long_alias,
+                      int code,
+                      const apr_getopt_option_t *option_table,
+                      const svn_opt_subcommand_desc3_t *command,
+                      apr_pool_t *pool)
+{
+  const apr_getopt_option_t *i;
+  const apr_getopt_option_t *opt
+    = svn_opt_get_option_from_code3(code, option_table, command, pool);
+
+  /* Find a long alias in the table, if there is one. */
+  *long_alias = NULL;
+  for (i = option_table; i->optch; i++)
+    {
+      if (i->optch == code && i->name != opt->name)
+        {
+          *long_alias = i->name;
+          break;
+        }
+    }
+
+  return opt;
+}
+
 /* Like svn_opt_get_option_from_code2(), but also, if CODE appears a second
  * time in OPTION_TABLE with a different name, then set *LONG_ALIAS to that
  * second name, else set it to NULL. */
@@ -205,6 +289,25 @@ svn_opt_format_option(const char **strin
 
 
 svn_boolean_t
+svn_opt_subcommand_takes_option4(const svn_opt_subcommand_desc3_t *command,
+                                 int option_code,
+                                 const int *global_options)
+{
+  apr_size_t i;
+
+  for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+    if (command->valid_options[i] == option_code)
+      return TRUE;
+
+  if (global_options)
+    for (i = 0; global_options[i]; i++)
+      if (global_options[i] == option_code)
+        return TRUE;
+
+  return FALSE;
+}
+
+svn_boolean_t
 svn_opt_subcommand_takes_option3(const svn_opt_subcommand_desc2_t *command,
                                  int option_code,
                                  const int *global_options)
@@ -251,6 +354,114 @@ svn_opt_subcommand_takes_option(const sv
    STREAM.  If HELP is set, print CMD's help string too, in which case
    obtain option usage from OPTIONS_TABLE. */
 static svn_error_t *
+print_command_info3(const svn_opt_subcommand_desc3_t *cmd,
+                    const apr_getopt_option_t *options_table,
+                    const int *global_options,
+                    svn_boolean_t help,
+                    apr_pool_t *pool,
+                    FILE *stream)
+{
+  svn_boolean_t first_time;
+  apr_size_t i;
+
+  /* Print the canonical command name. */
+  SVN_ERR(svn_cmdline_fputs(cmd->name, stream, pool));
+
+  /* Print the list of aliases. */
+  first_time = TRUE;
+  for (i = 0; i < SVN_OPT_MAX_ALIASES; i++)
+    {
+      if (cmd->aliases[i] == NULL)
+        break;
+
+      if (first_time) {
+        SVN_ERR(svn_cmdline_fputs(" (", stream, pool));
+        first_time = FALSE;
+      }
+      else
+        SVN_ERR(svn_cmdline_fputs(", ", stream, pool));
+
+      SVN_ERR(svn_cmdline_fputs(cmd->aliases[i], stream, pool));
+    }
+
+  if (! first_time)
+    SVN_ERR(svn_cmdline_fputs(")", stream, pool));
+
+  if (help)
+    {
+      const apr_getopt_option_t *option;
+      const char *long_alias;
+      svn_boolean_t have_options = FALSE;
+
+      SVN_ERR(svn_cmdline_fprintf(stream, pool, ": "));
+
+      for (i = 0; i < SVN_OPT_MAX_PARAGRAPHS && cmd->help[i]; i++)
+        {
+          SVN_ERR(svn_cmdline_fprintf(stream, pool, "%s", _(cmd->help[i])));
+        }
+
+      /* Loop over all valid option codes attached to the subcommand */
+      for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+        {
+          if (cmd->valid_options[i])
+            {
+              if (!have_options)
+                {
+                  SVN_ERR(svn_cmdline_fputs(_("\nValid options:\n"),
+                                            stream, pool));
+                  have_options = TRUE;
+                }
+
+              /* convert each option code into an option */
+              option = get_option_from_code3(&long_alias, 
cmd->valid_options[i],
+                                             options_table, cmd, pool);
+
+              /* print the option's docstring */
+              if (option && option->description)
+                {
+                  const char *optstr;
+                  format_option(&optstr, option, long_alias, TRUE, pool);
+                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
+                                              optstr));
+                }
+            }
+        }
+      /* And global options too */
+      if (global_options && *global_options)
+        {
+          SVN_ERR(svn_cmdline_fputs(_("\nGlobal options:\n"),
+                                    stream, pool));
+          have_options = TRUE;
+
+          for (i = 0; global_options[i]; i++)
+            {
+
+              /* convert each option code into an option */
+              option = get_option_from_code3(&long_alias, global_options[i],
+                                             options_table, cmd, pool);
+
+              /* print the option's docstring */
+              if (option && option->description)
+                {
+                  const char *optstr;
+                  format_option(&optstr, option, long_alias, TRUE, pool);
+                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
+                                              optstr));
+                }
+            }
+        }
+
+      if (have_options)
+        SVN_ERR(svn_cmdline_fprintf(stream, pool, "\n"));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Print the canonical command name for CMD, and all its aliases, to
+   STREAM.  If HELP is set, print CMD's help string too, in which case
+   obtain option usage from OPTIONS_TABLE. */
+static svn_error_t *
 print_command_info2(const svn_opt_subcommand_desc2_t *cmd,
                     const apr_getopt_option_t *options_table,
                     const int *global_options,
@@ -350,6 +561,38 @@ print_command_info2(const svn_opt_subcom
   return SVN_NO_ERROR;
 }
 
+/* The body for svn_opt_print_generic_help3() function with standard error
+ * handling semantic. Handling of errors implemented at caller side. */
+static svn_error_t *
+print_generic_help_body3(const char *header,
+                         const svn_opt_subcommand_desc3_t *cmd_table,
+                         const apr_getopt_option_t *opt_table,
+                         const char *footer,
+                         apr_pool_t *pool, FILE *stream)
+{
+  int i = 0;
+
+  if (header)
+    SVN_ERR(svn_cmdline_fputs(header, stream, pool));
+
+  while (cmd_table[i].name)
+    {
+      SVN_ERR(svn_cmdline_fputs("   ", stream, pool));
+      SVN_ERR(print_command_info3(cmd_table + i, opt_table,
+                                  NULL, FALSE,
+                                  pool, stream));
+      SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
+      i++;
+    }
+
+  SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
+
+  if (footer)
+    SVN_ERR(svn_cmdline_fputs(footer, stream, pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* The body for svn_opt_print_generic_help2() function with standard error
  * handling semantic. Handling of errors implemented at caller side. */
 static svn_error_t *
@@ -383,6 +626,30 @@ print_generic_help_body(const char *head
 }
 
 void
+svn_opt_print_generic_help3(const char *header,
+                            const svn_opt_subcommand_desc3_t *cmd_table,
+                            const apr_getopt_option_t *opt_table,
+                            const char *footer,
+                            apr_pool_t *pool, FILE *stream)
+{
+  svn_error_t *err;
+
+  err = print_generic_help_body3(header, cmd_table, opt_table, footer, pool,
+                                 stream);
+
+  /* Issue #3014:
+   * Don't print anything on broken pipes. The pipe was likely
+   * closed by the process at the other end. We expect that
+   * process to perform error reporting as necessary.
+   *
+   * ### This assumes that there is only one error in a chain for
+   * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
+  if (err && err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+    svn_handle_error2(err, stderr, FALSE, "svn: ");
+  svn_error_clear(err);
+}
+
+void
 svn_opt_print_generic_help2(const char *header,
                             const svn_opt_subcommand_desc2_t *cmd_table,
                             const apr_getopt_option_t *opt_table,
@@ -408,6 +675,32 @@ svn_opt_print_generic_help2(const char *
 
 
 void
+svn_opt_subcommand_help4(const char *subcommand,
+                         const svn_opt_subcommand_desc3_t *table,
+                         const apr_getopt_option_t *options_table,
+                         const int *global_options,
+                         apr_pool_t *pool)
+{
+  const svn_opt_subcommand_desc3_t *cmd =
+    svn_opt_get_canonical_subcommand3(table, subcommand);
+  svn_error_t *err;
+
+  if (cmd)
+    err = print_command_info3(cmd, options_table, global_options,
+                              TRUE, pool, stdout);
+  else
+    err = svn_cmdline_fprintf(stderr, pool,
+                              _("\"%s\": unknown command.\n\n"), subcommand);
+
+  if (err) {
+    /* Issue #3014: Don't print anything on broken pipes. */
+    if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+      svn_handle_error2(err, stderr, FALSE, "svn: ");
+    svn_error_clear(err);
+  }
+}
+
+void
 svn_opt_subcommand_help3(const char *subcommand,
                          const svn_opt_subcommand_desc2_t *table,
                          const apr_getopt_option_t *options_table,
@@ -1192,6 +1485,56 @@ svn_opt__print_version_info(const char *
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_opt_print_help5(apr_getopt_t *os,
+                    const char *pgm_name,
+                    svn_boolean_t print_version,
+                    svn_boolean_t quiet,
+                    svn_boolean_t verbose,
+                    const char *version_footer,
+                    const char *header,
+                    const svn_opt_subcommand_desc3_t *cmd_table,
+                    const apr_getopt_option_t *option_table,
+                    const int *global_options,
+                    const char *footer,
+                    apr_pool_t *pool)
+{
+  apr_array_header_t *targets = NULL;
+
+  if (os)
+    SVN_ERR(svn_opt_parse_all_args(&targets, os, pool));
+
+  if (os && targets->nelts)  /* help on subcommand(s) requested */
+    {
+      int i;
+
+      for (i = 0; i < targets->nelts; i++)
+        {
+          svn_opt_subcommand_help4(APR_ARRAY_IDX(targets, i, const char *),
+                                   cmd_table, option_table,
+                                   global_options, pool);
+        }
+    }
+  else if (print_version)   /* just --version */
+    {
+      SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
+                                          svn_version_extended(verbose, pool),
+                                          quiet, verbose, pool));
+    }
+  else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
+    svn_opt_print_generic_help3(header,
+                                cmd_table,
+                                option_table,
+                                footer,
+                                pool,
+                                stdout);
+  else                                       /* unknown option or cmd */
+    SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+                                _("Type '%s help' for usage.\n"), pgm_name));
+
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_opt_print_help4(apr_getopt_t *os,


Reply via email to