Rob Siemborski wrote:
> 
> On Thu, 25 Jul 2002, Blair Zajac wrote:
> 
> > I started a patch to factor out the usage stuff for svnadmin and
> > svnlook, but it's way out of date, back in April revision 1642.  If
> > somebody wants to pick it up where I stopped, I can send the diffs
> > out.  I'm afraid they'll need a ton of integration into the current
> > tree.
> 
> Since I started this whole thing, and it sounds simple enough (from a
> getting-to-know-the-code perspective), I'd be willing to take a look if
> you sent me the diffs.
Here you go.  I don't have a log message yet for the work that I started.
The hardest part is that svn passes around a pointer to a large structure
specific for itself and its subcommands that I changed to a void * so that
it could be used by svnadmin and svnlook.  There are also some static
functions for svn that needed to get moved into a public library and I
choose libsvn_subr.
The diff is large but a lot of it is just moving code around from one
file to another to get static functions into the library.  I also
wouldn't try to apply it to the current tree, rather just copy what
was done and use the current tree.
Best,
Blair
-- 
Blair Zajac <blair@orcaware.com>
Web and OS performance plots - http://www.orcaware.com/orca/
Index: ./subversion/include/svn_getopt.h
===================================================================
--- ./subversion/include/svn_getopt.h
+++ ./subversion/include/svn_getopt.h	Fri Mar 15 13:21:34 2002
@@ -0,0 +1,281 @@
+/*
+ * svn_getopt.h:  command line processing and usage utilities
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2002 CollabNet.  All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+#ifndef SVN_GETOPT_H
+#define SVN_GETOPT_H
+
+/*** Includes. ***/
+#include "apr_getopt.h"
+
+#include "svn_error.h"
+
+/*** Defines. ***/
+
+/* This is the maximum number of aliases a command can have. */
+#define SVN_CL__MAX_ALIASES 3
+
+/* This is the maximum number of options accepted by a single command.
+   Each value in the array is a unique enum which is the 2nd field in
+   the apr_getopt_option_t. */
+#define SVN_CL__MAX_OPTS 15
+
+
+/*** Command dispatch. ***/
+
+/* All client command functions conform to this prototype.  ARGS
+ * contains the list of non command line options passed to the
+ * program.  Most of the time ARGS will be a list of filenames and
+ * directories, a-la CVS (which really only becomes useful if you pass
+ * it into svn_cl__args_to_target_array() to convert ARGS to an APR
+ * array of svn_stringbuf_t * targets).  To enable different programs
+ * to do common command line preprocessing for multiple command
+ * functions before calling the appropriate command function, OPT_DATA
+ * is a void * that can be used to pass arbitrary data in.
+ */
+struct svn_getopt_t;
+typedef svn_error_t *(svn_cl__cmd_proc_t) (struct svn_getopt_t *args,
+                                           void *opt_data,
+                                           apr_pool_t *pool);
+
+
+/* One element of the command dispatch table.  The caller creates a
+   table of these for each command that the program supports.  */
+typedef struct svn_cl__cmd_desc_t
+{
+  /* The full name of this command. */
+  const char *name;
+
+  /* The function this command invokes. */
+  svn_cl__cmd_proc_t *cmd_func;
+
+  /* A list of alias names for this command. */
+  const char *aliases[SVN_CL__MAX_ALIASES+1];
+
+  /* A brief string describing this command, for usage messages. */
+  const char *help;
+
+  /* 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_CL__MAX_OPTS];
+} svn_cl__cmd_desc_t;
+
+typedef struct svn_getopt_t {
+  apr_getopt_t *options;
+  const apr_getopt_option_t *option_table;
+  const svn_cl__cmd_desc_t *command_table;
+  int num_received_opts;
+  int received_opts[SVN_CL__MAX_OPTS];
+  const char* received_args[SVN_CL__MAX_OPTS];
+  const svn_cl__cmd_desc_t *subcommand;
+} svn_getopt_t;
+
+/*** Functions. ***/
+
+/* Initialize the arguments for parsing.  This should be used in place
+   of apr_getopt_init().  */
+apr_status_t
+svn_getopt_init(svn_getopt_t **os, apr_pool_t *pool,
+                int argc, const char * const *argv,
+                const apr_getopt_option_t *option_table,
+                const svn_cl__cmd_desc_t *command_table);
+
+svn_error_t *
+svn_cl__help (svn_getopt_t *os, void *help_info_, apr_pool_t *pool);
+
+/* Given a command name COMMAND_NAME and a table of command dispatch
+   functions (svn_cl__cmd_desc_t[]), COMMAND_TABLE, and a pool POOL,
+   print the usage of the command.  This function is also used by
+   subcommands that need to print a usage message.  */
+void
+svn_cl__print_subcommand_help (const char *command_name,
+                               const svn_getopt_t *os,
+                               apr_pool_t *pool);
+
+/* Given a command name COMMAND_NAME and a table of command dispatch
+   functions (svn_cl__cmd_desc_t[]), COMMAND_TABLE, return a pointer
+   to the svn_cl__cmd_desc_t whose name matches COMMAND_NAME or NULL if
+   none mathces.  COMMAND_NAME may be an alias. */
+const svn_cl__cmd_desc_t *
+svn_cl__get_canonical_command (const char *command_name,
+                               const svn_cl__cmd_desc_t *command_table);
+
+/* Create a targets array and add all the remaining arguments
+   to it. We also process arguments passed in the --target file, if
+   specified, just as if they were passed on the command line.  */
+apr_array_header_t *
+svn_cl__args_to_target_array (const svn_getopt_t *os,
+                              apr_array_header_t *cmd_targets,
+                              apr_pool_t *pool);
+
+/* Print the canonical command name for CMD, all its aliases,
+   and if HELP is set, print the help string for the command too. */
+void
+svn_cl__print_command_info (const svn_cl__cmd_desc_t *cmd_desc,
+                            const svn_getopt_t *os,
+                            svn_boolean_t help, 
+                            apr_pool_t *pool,
+                            FILE *stream);
+
+/* Look up CODE in OPTION_TABLE.  If any option in the table has this
+   enum code, return a pointer to the option.  Else return NULL.  */
+const apr_getopt_option_t *
+svn_cl__get_option_from_enum (int code,
+                              const apr_getopt_option_t *option_table);
+
+
+
+/*** Miscellaneous utility commands ***/
+
+/* Parse all of the arguments from the command line args passed in by
+   the user.  Put them into ARGS.  */
+svn_error_t *
+svn_cl__parse_all_args (apr_array_header_t **args,
+                        const svn_getopt_t *os,
+                        const char *subcommand,
+                        apr_pool_t *pool);
+
+/* Parse a given number of non-target arguments from the command line
+   args passed in by the user.  Put them into the passed in ARGS
+   array.  */
+svn_error_t *
+svn_cl__parse_num_args (apr_array_header_t **args,
+                        const svn_getopt_t *os,
+                        const char *subcommand,
+                        int num_args,
+                        apr_pool_t *pool);
+
+/* Print an option OPT nicely into a STRING allocated in POOL.  If DOC
+   is set, include generic documentation string of option.*/
+void
+svn_cl__format_option (char **string,
+                       const apr_getopt_option_t *opt,
+                       svn_boolean_t doc,
+                       apr_pool_t *pool);
+
+/* Create a SVN string from the char* and add it to the array.  */
+void 
+array_push_svn_stringbuf (apr_array_header_t *array,
+                          const char *str,
+                          apr_pool_t *pool);
+
+#if 0
+
+/* Look up CODE in OPTION_TABLE.   If any option in the table has this
+   enum code, return a pointer to the option.  Else return NULL. */
+const apr_getopt_option_t *
+svn_cl__get_option_from_enum (int code,
+                              const apr_getopt_option_t *option_table);
+
+
+void svn_cl__push_implicit_dot_target (apr_array_header_t *targets,
+                                       apr_pool_t *pool);
+
+
+void
+svn_cl__subcommand_help (const char *subcommand,
+                         apr_pool_t *pool);
+
+
+/*** Command-line output functions -- printing to the user. ***/
+
+/* Print a hash that maps property names (char *) to property values
+   (svn_stringbuf_t *). */
+void svn_cl__print_prop_hash (apr_hash_t *prop_hash, apr_pool_t *pool);
+
+/* Print out the property names in a hash that maps property names (char *) 
+   to property values (svn_stringbuf_t *). */
+void svn_cl__print_prop_names (apr_hash_t *prop_hash, apr_pool_t *pool);
+
+/* Returns an editor that prints out events in an update or checkout. */
+svn_error_t *
+svn_cl__get_trace_update_editor (const svn_delta_edit_fns_t **editor,
+                                 void **edit_baton,
+                                 svn_stringbuf_t *initial_path,
+                                 apr_pool_t *pool);
+
+/* Returns an editor that prints out events in a commit. */
+svn_error_t *
+svn_cl__get_trace_commit_editor (const svn_delta_edit_fns_t **editor,
+                                 void **edit_baton,
+                                 svn_stringbuf_t *initial_path,
+                                 apr_pool_t *pool);
+
+
+/* Search for a text editor command in standard environment variables,
+   and invoke it to edit CONTENTS (using a temporary file based on
+   working copy directory BASE_DIR).  Return the new contents in
+   EDITED_CONTENTS, or set EDITED_CONTENTS to NULL if no edit was
+   performed.  Use POOL for all allocations. */
+svn_error_t *
+svn_cl__edit_externally (svn_stringbuf_t **edited_contents,
+                         svn_stringbuf_t *base_dir,
+                         const svn_string_t *contents,
+                         apr_pool_t *pool);
+
+
+/* Our implementation of the 'auth info callback' routine, 
+   as defined in svn_client.h.   This callback is passed to any
+   libsvn_client routine that needs to authenticate against a
+   repository. */
+
+/* Display PROMPT to the user, and read a reply back from stdin,
+   allocated in POOL and returned in *RESULT.  If HIDE is set, the
+   reply will not be echoed to the screen.  BATON is ignored (but
+   required by the definition of svn_client_auth_info_callback_t.) */
+svn_error_t *
+svn_cl__prompt_user (char **result,
+                     const char *prompt,
+                     svn_boolean_t hide,
+                     void *baton,
+                     apr_pool_t *pool);
+
+/* Helper for subcommands: given parsed OPT_STATE arguments from the
+   command-line, put auth info into a structure to pass to libsvn_client. */
+svn_client_auth_baton_t *
+svn_cl__make_auth_baton (svn_cl__opt_state_t *opt_state,
+                         apr_pool_t *pool);
+
+/* Fills in the first four characters of STR_STATUS with status code
+   characters, based on TEXT_STATUS, PROP_STATUS, LOCKED, and COPIED.*/
+void
+svn_cl__generate_status_codes (char *str_status,
+                               enum svn_wc_status_kind text_status,
+                               enum svn_wc_status_kind prop_status,
+                               svn_boolean_t locked,
+                               svn_boolean_t copied);
+
+
+/*** Notification functions to display results on the terminal. */
+
+void svn_cl__notify_func (void *baton, 
+                          svn_wc_notify_action_t action,
+                          const char *path);
+void *svn_cl__make_notify_baton (apr_pool_t *pool);
+
+#endif
+
+#endif /* SVN_GETOPT_H */
+
+/* 
+ * local variables:
+ * eval: (load-file "../../tools/dev/svn-dev.el")
+ * end: 
+ */
Index: ./subversion/libsvn_subr/svn_getopt.c
===================================================================
--- ./subversion/libsvn_subr/svn_getopt.c
+++ ./subversion/libsvn_subr/svn_getopt.c	Fri Mar 15 13:21:54 2002
@@ -0,0 +1,546 @@
+/*
+ * svn_getopt.c:  command line processing and usage utility functions
+ *
+ * ====================================================================
+ * Copyright (c) 2000-2002 CollabNet.  All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.  The terms
+ * are also available at http://subversion.tigris.org/license-1.html.
+ * If newer versions of this license are posted there, you may use a
+ * newer version instead, at your option.
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals.  For exact contribution history, see the revision
+ * history and logs, available at http://subversion.tigris.org/.
+ * ====================================================================
+ */
+
+
+
+#include "svn_getopt.h"
+#include "svn_path.h"
+#include "svn_string.h"
+#include "svn_wc.h"
+#include "svn_version.h"
+
+#define DEFAULT_ARRAY_SIZE 5
+
+/* Return TRUE iff subcommand COMMAND has OPTION_CODE listed within
+   it.  Else return FALSE. */
+static svn_boolean_t
+subcommand_takes_option (const svn_cl__cmd_desc_t *command,
+                         int option_code)
+{
+  int i;
+  
+  for (i = 0; i < SVN_CL__MAX_OPTS; i++)
+    {          
+      if (command->valid_options[i] == option_code)
+        return TRUE;
+    }
+  return FALSE;
+}
+
+
+static svn_error_t *
+print_version_info (apr_pool_t *pool)
+{
+  void *ra_baton;
+  svn_stringbuf_t *descriptions;
+  static const char info[] =
+    "Copyright (C) 2000-2002 CollabNet.\n"
+    "Subversion is open source software, see http://subversion.tigris.org/\n";
+
+  printf ("Subversion Client, version %s\n", SVN_VERSION);
+  printf ("   compiled %s, %s\n\n", __DATE__, __TIME__);
+  printf ("%s\n", info);
+
+  printf ("The following repository access (RA) modules are available:\n\n");
+
+  /* Get a hash full of all available RA libraries.  */
+  SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
+
+  /* Get a descriptive list of them. */
+  SVN_ERR (svn_ra_print_ra_libraries (&descriptions, ra_baton, pool));
+
+  printf ("%s\n", descriptions->data);
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Print a generic (non-command-specific) usage message. */
+static void
+svn_cl__print_generic_help (svn_getopt_t *os, apr_pool_t *pool, FILE *stream)
+{
+  static const char usage[] =
+    "usage: svn <subcommand> [options] [args]\n"
+    "Type \"svn help <subcommand>\" for help on a specific subcommand.\n"
+    "\n"
+    "Most subcommands take file and/or directory arguments, recursing\n"
+    "on the directories.  If no arguments are supplied to such a\n"
+    "command, it will recurse on the current directory (inclusive) by\n" 
+    "default.\n"
+    "\n"
+    "Available subcommands:\n";
+
+  static const char info[] =
+    "Subversion is a tool for revision control.\n"
+    "For additional information, see http://subversion.tigris.org\n";
+
+  int i = 0;
+
+  fprintf (stream, "%s", usage);
+
+  if (os)
+    {
+      const svn_cl__cmd_desc_t *command_table = os->command_table;
+      while (command_table[i].name) 
+        {
+          fprintf (stream, "   ");
+          svn_cl__print_command_info (command_table + i, os, FALSE, pool, stream);
+          fprintf (stream, "\n");
+          i++;
+        }
+    }
+
+  fprintf (stream, "\n");
+  fprintf (stream, "%s\n", info);
+}
+
+
+/* Print either generic help, or command-specific help for each
+ * command in os->args.  OPT_STATE is only examined for the
+ * '--version' switch.  If OS is null then generic help will always be
+ * printed.
+ * 
+ * Unlike all the other command routines, ``help'' has its own
+ * option processing.
+ */
+typedef struct help_info_t {
+  apr_array_header_t *cmd_targets;
+  int version;
+} help_info_t;
+
+svn_error_t *
+svn_cl__help (svn_getopt_t *os,
+              void *help_info_,
+              apr_pool_t *pool)
+{
+  help_info_t *help_info = (help_info_t *)help_info_;
+  apr_array_header_t *targets = NULL;
+  int i;
+
+  if (os)
+    targets = svn_cl__args_to_target_array (os,
+                                            help_info ? help_info->cmd_targets : NULL,
+                                            pool);
+
+  if (targets && targets->nelts)  /* help on subcommand(s) requested */
+    for (i = 0; i < targets->nelts; i++)
+      {
+        svn_stringbuf_t *this = (((svn_stringbuf_t **) (targets)->elts))[i];
+        svn_cl__print_subcommand_help (this->data, os, pool);
+      }
+  else if (help_info && help_info->version)  /* just -v or --version */
+    SVN_ERR (print_version_info (pool));        
+  else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
+    svn_cl__print_generic_help (os, pool, stdout);  
+  else                                       /* unknown option or cmd */
+    svn_cl__print_generic_help (os, pool, stderr);
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Initialize the arguments for parsing.  This should be used in place
+   of apr_getopt_init().  */
+apr_status_t
+svn_getopt_init(svn_getopt_t **os, apr_pool_t *pool,
+                int argc, const char * const *argv,
+                const apr_getopt_option_t *option_table,
+                const svn_cl__cmd_desc_t *command_table)
+{
+  apr_status_t apr_err;
+  const char *first_arg;
+  int i;
+
+  *os = apr_palloc(pool, sizeof(**os));
+  (*os)->option_table = option_table;
+  (*os)->command_table = command_table;
+  (*os)->num_received_opts = 0;
+  (*os)->received_args[0] = 0;
+  (*os)->received_opts[0] = 0;
+
+  apr_err = apr_getopt_init (&((*os)->options), pool, argc, argv);
+  if (apr_err != APR_SUCCESS) {
+    return apr_err;
+  }
+
+  /* Parse the options. */
+  (*os)->options->interleave = 1;
+  while (1)
+    {
+      int opt_id;
+      const char *opt_arg;
+
+      /* Parse the next option. */
+      apr_err = apr_getopt_long ((*os)->options, option_table,
+                                 &opt_id, &opt_arg);
+      if (APR_STATUS_IS_EOF (apr_err))
+        break;
+      else if (! APR_STATUS_IS_SUCCESS (apr_err))
+        {
+          svn_cl__help (NULL, NULL, pool);
+          return EXIT_FAILURE;
+        }
+
+      /* Stash the option code in an array before parsing it. */
+      (*os)->received_args[(*os)->num_received_opts] = opt_arg;
+      (*os)->received_opts[(*os)->num_received_opts] = opt_id;
+      ((*os)->num_received_opts)++;
+    }
+
+  /* Check if there are any remaining arguments on the command line. */
+  if ((*os)->options->ind >= (*os)->options->argc) {
+    fprintf (stderr, "subcommand argument required\n");
+    return EXIT_FAILURE;
+  }
+
+  /* Look for a subcommand in the first argument. */
+  first_arg = (*os)->options->argv[(*os)->options->ind++];
+  (*os)->subcommand = svn_cl__get_canonical_command (first_arg,
+                                                     command_table);
+  if ((*os)->subcommand == NULL)
+    {
+      /* FIXME: should we print "unknown foo" ?? seems ok */
+      fprintf (stderr, "unknown command: %s\n", first_arg);
+      svn_cl__help (NULL, NULL, pool);
+      return EXIT_FAILURE;
+    }
+
+  /* Check if this subcommand wasn't passed any inappropriate
+     options. */
+  for (i=0; i<(*os)->num_received_opts; i++)
+    if (! subcommand_takes_option ((*os)->subcommand, (*os)->received_opts[i]))
+      {
+        char *optstr;
+        const apr_getopt_option_t *badopt = 
+          svn_cl__get_option_from_enum ((*os)->received_opts[i], option_table);
+        svn_cl__format_option (&optstr, badopt, FALSE, pool);
+        fprintf (stderr,
+                 "\nError: subcommand '%s' doesn't accept option '%s'\n\n",
+                 (*os)->subcommand->name, optstr);
+        svn_cl__print_subcommand_help ((*os)->subcommand->name, *os, pool);
+        return EXIT_FAILURE;
+      }
+
+  return APR_SUCCESS;
+}
+
+
+/* Given a command name COMMAND_NAME and a table of command dispatch
+   functions (svn_cl__cmd_desc_t[]), COMMAND_TABLE, return a pointer
+   to the svn_cl__cmd_desc_t whose name matches COMMAND_NAME or NULL
+   if none mathces.  COMMAND_NAME may be an alias.  */
+const svn_cl__cmd_desc_t *
+svn_cl__get_canonical_command (const char *command_name,
+                               const svn_cl__cmd_desc_t *command_table)
+{
+  int i = 0;
+
+  if (command_name == NULL)
+    return NULL;
+
+  while (command_table[i].name) {
+    int j;
+    if (strcmp (command_name, command_table[i].name) == 0)
+      return command_table + i;
+    for (j = 0; 
+         (j < SVN_CL__MAX_ALIASES) && command_table[i].aliases[j]; 
+         j++)
+      if (strcmp (command_name, command_table[i].aliases[j]) == 0)
+        return command_table + i;
+
+    i++;
+  }
+
+  /* If we get here, there was no matching command name or alias. */
+  return NULL;
+}
+
+/* Create a targets array and add all the remaining arguments
+   to it. We also process arguments passed in the --target file, if
+   specified, just as if they were passed on the command line.  */
+apr_array_header_t *
+svn_cl__args_to_target_array (const svn_getopt_t *os,
+                              apr_array_header_t *cmd_targets,
+                              apr_pool_t *pool)
+{
+  apr_getopt_t *options = os->options;
+  apr_array_header_t *targets =
+    apr_array_make (pool, DEFAULT_ARRAY_SIZE, sizeof (svn_stringbuf_t *));
+
+  /* Command line args take precendence.  */
+  for (; options->ind < options->argc; options->ind++)
+    {
+      svn_stringbuf_t *target = svn_stringbuf_create (options->argv[options->ind],
+                                                      pool);
+      svn_string_t tstr;
+
+      /* If this path looks like it would work as a URL in one of the
+         currently available RA libraries, we add it unconditionally
+         to the target array. */
+      tstr.data = target->data;
+      tstr.len  = target->len;
+      if (! svn_path_is_url (&tstr))
+        {
+          const char *basename = svn_path_basename (target->data, pool);
+
+          /* If this target is a Subversion administrative directory,
+             skip it.  TODO: Perhaps this check should not call the
+             target a SVN admin dir unless svn_wc_check_wc passes on
+             the target, too? */
+          if (! strcmp (basename, SVN_WC_ADM_DIR_NAME))
+            continue;
+        }
+      else
+        {
+          svn_path_canonicalize (target);
+        }
+      (*((svn_stringbuf_t **) apr_array_push (targets))) = target;
+    }
+
+  /* Now args from --targets, if any */
+  if (NULL != cmd_targets)
+    apr_array_cat(targets, cmd_targets);
+
+  /* kff todo: need to remove redundancies from targets before
+     passing it to the cmd_func. */
+     
+  return targets;
+}
+
+/* Given a command name COMMAND_NAME and a table of command dispatch
+   functions (svn_cl__cmd_desc_t[]), COMMAND_TABLE, and a pool POOL,
+   print the usage of the command.  This function is also used by
+   subcommands that need to print a usage message.  */
+void
+svn_cl__print_subcommand_help (const char* command_name,
+                               const svn_getopt_t *os,
+                               apr_pool_t *pool)
+{
+  const svn_cl__cmd_desc_t *cmd =
+    svn_cl__get_canonical_command (command_name, os->command_table);
+    
+  if (cmd)
+    svn_cl__print_command_info (cmd, os, TRUE, pool, stdout);
+  else
+    fprintf (stderr, "\"%s\": unknown command.\n\n", command_name);
+}
+
+
+/* Print an option OPT nicely into a STRING allocated in POOL.  If DOC
+   is set, include generic documentation string of option.*/
+void
+svn_cl__format_option (char **string,
+                       const apr_getopt_option_t *opt,
+                       svn_boolean_t doc,
+                       apr_pool_t *pool)
+{
+  char *opts;
+
+  if (opt == NULL)
+    *string = apr_psprintf (pool, "?");
+
+  if (opt->optch <= 255)  
+    opts = apr_psprintf (pool, "-%c [--%s]", opt->optch, opt->name);
+  else
+    opts = apr_psprintf (pool, "--%s", opt->name);
+
+  if (opt->has_arg)
+    opts = apr_pstrcat (pool, opts, " arg", NULL);
+  
+  if (doc)
+    opts = apr_pstrcat (pool, opts, ":\t", opt->description, NULL);
+
+  *string = opts;
+}
+
+/* Print the canonical command name for CMD, all its aliases,
+   and if HELP is set, print the help string for the command too. */
+void
+svn_cl__print_command_info (const svn_cl__cmd_desc_t *cmd_desc,
+                            const svn_getopt_t *os,
+                            svn_boolean_t help, 
+                            apr_pool_t *pool,
+                            FILE *stream)
+{
+  const apr_getopt_option_t *option_table = os->option_table;
+  const svn_cl__cmd_desc_t *command_table = os->command_table;
+  const svn_cl__cmd_desc_t *canonical_cmd
+    = svn_cl__get_canonical_command (cmd_desc->name, command_table);
+  svn_boolean_t first_time;
+  int i;
+
+  /* Print the canonical command name. */
+  fputs (canonical_cmd->name, stream);
+
+  /* Print the list of aliases. */
+  first_time = TRUE;
+  for (i = 0; i < SVN_CL__MAX_ALIASES; i++) 
+    {
+      if (canonical_cmd->aliases[i] == NULL)
+        break;
+
+      if (first_time) {
+        fprintf (stream, " (");
+        first_time = FALSE;
+      }
+      else
+        fprintf (stream, ", ");
+      
+      fprintf (stream, "%s", canonical_cmd->aliases[i]);
+    }
+
+  if (! first_time)
+    fprintf (stream, ")");
+  
+  if (help)
+    {
+      const apr_getopt_option_t *option;
+      svn_boolean_t have_options = FALSE;
+
+      fprintf (stream, ": %s", canonical_cmd->help);
+
+      /* Loop over all valid option codes attached to the subcommand */
+      for (i = 0; i < SVN_CL__MAX_OPTS; i++)
+        {
+          if (canonical_cmd->valid_options[i])
+            {
+              if (have_options == FALSE)
+                {
+                  fprintf (stream, "\nValid options:\n");
+                  have_options = TRUE;
+                }
+
+              /* convert each option code into an option */
+              option = 
+                svn_cl__get_option_from_enum (canonical_cmd->valid_options[i],
+                                              option_table);
+
+              /* print the option's docstring */
+              if (option)
+                {
+                  char *optstr;
+                  svn_cl__format_option (&optstr, option, TRUE, pool);
+                  fprintf (stream, "  %s\n", optstr);
+                }
+            }
+        }
+
+      if (have_options)
+        fprintf (stream, "\n");
+    }
+}
+
+const apr_getopt_option_t *
+svn_cl__get_option_from_enum (int code,
+                              const apr_getopt_option_t *option_table)
+{
+  int i;
+  const apr_getopt_option_t *opt = NULL;
+
+  for (i = 0; i < SVN_CL__MAX_OPTS; i++)
+    {
+      if (option_table[i].optch == code)
+        {
+          opt = &(option_table[i]);
+          break;
+        }
+    }
+  
+  return opt;
+}
+
+/* Create a SVN string from the char* and add it to the array.  */
+void 
+array_push_svn_stringbuf (apr_array_header_t *array,
+                          const char *str,
+                          apr_pool_t *pool)
+{
+  (*((svn_stringbuf_t **) apr_array_push (array)))
+    = svn_stringbuf_create (str, pool);
+}
+
+/* Parse all of the arguments from the command line args passed in by
+   the user.  Put them into ARGS.  */
+svn_error_t *
+svn_cl__parse_all_args (apr_array_header_t **args,
+                        const svn_getopt_t *os,
+                        const char *subcommand,
+                        apr_pool_t *pool)
+{
+  apr_getopt_t *options = os->options;
+
+  *args = apr_array_make (pool,
+                          DEFAULT_ARRAY_SIZE, 
+                          sizeof (svn_stringbuf_t *));
+
+  if (options->ind >= options->argc)
+    {
+      svn_cl__print_subcommand_help (subcommand, os, pool);
+      return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
+    }
+
+  while (options->ind < options->argc)
+    {
+      array_push_svn_stringbuf (*args,
+                                options->argv[options->ind++],
+                                pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Parse a given number of non-target arguments from the command line
+   args passed in by the user.  Put them into the passed in ARGS
+   array.  */
+svn_error_t *
+svn_cl__parse_num_args (apr_array_header_t **args,
+                        const svn_getopt_t *os,
+                        const char *subcommand,
+                        int num_args,
+                        apr_pool_t *pool)
+{
+  apr_getopt_t *options = os->options;
+  int i;
+
+  *args = apr_array_make (pool,
+                          DEFAULT_ARRAY_SIZE,
+                          sizeof (svn_stringbuf_t *));
+
+  /* loop for num_args and add each arg to the args array */
+  for (i = 0; i < num_args; i++)
+    {
+      if (options->ind >= options->argc)
+        {
+          svn_cl__print_subcommand_help (subcommand, os, pool);
+          return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 
+                                   0, 0, pool, "");
+        }
+      array_push_svn_stringbuf (*args,
+                                options->argv[options->ind++],
+                                pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/* --------------------------------------------------------------
+ * local variables:
+ * eval: (load-file "../../tools/dev/svn-dev.el")
+ * end:
+ */
Index: ./subversion/clients/cmdline/merge-cmd.c
===================================================================
--- ./subversion/clients/cmdline/merge-cmd.c
+++ ./subversion/clients/cmdline/merge-cmd.c	Thu Apr  4 17:17:13 2002
@@ -35,10 +35,11 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__merge (apr_getopt_t *os,
-               svn_cl__opt_state_t *opt_state,
+svn_cl__merge (svn_getopt_t *os,
+               void *opt_state_,
                apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *options;
   apr_array_header_t *targets;
   apr_array_header_t *condensed_targets;
@@ -47,7 +48,7 @@
 
   options = svn_cl__stringlist_to_array (opt_state->extensions, pool);
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
   svn_cl__push_implicit_dot_target (targets, pool);
   SVN_ERR (svn_path_remove_redundancies (&condensed_targets,
                                          targets,
Index: ./subversion/clients/cmdline/cl.h
===================================================================
--- ./subversion/clients/cmdline/cl.h
+++ ./subversion/clients/cmdline/cl.h	Thu Apr  4 16:13:23 2002
@@ -25,8 +25,8 @@
 
 /*** Includes. ***/
 #include <apr_tables.h>
-#include <apr_getopt.h>
 
+#include "svn_getopt.h"
 #include "svn_wc.h"
 #include "svn_client.h"
 #include "svn_string.h"
@@ -84,7 +84,6 @@
   svn_boolean_t verbose;
   svn_boolean_t very_verbose;
   svn_boolean_t update;
-  apr_array_header_t *args;
   /* TODO fixme. This still doesn't handle binary data from a file! */
   svn_stringbuf_t *filedata;
   svn_boolean_t help;
@@ -97,25 +96,6 @@
   apr_array_header_t *targets;
 } svn_cl__opt_state_t;
 
-
-/* All client command procedures conform to this prototype.  OPT_STATE
- * likewise should hold the result of processing the options.  OS is a
- * list of filenames and directories, a-la CVS (which really only
- * becomes useful if you pass it into svn_cl__args_to_target_array()
- * to convert OS to an APR arra of svn_stringbuf_t * targets).
- *
- * TARGETS is normalized by main before being passed to any command
- * (with the exception of svn_cl__help, which will oftentime be passed
- * an empty array of targets. That is, all duplicates are removed, and
- * all paths are made relative to the working copy root directory). */
-typedef svn_error_t *(svn_cl__cmd_proc_t) (apr_getopt_t *os,
-                                           svn_cl__opt_state_t *opt_state,
-                                           apr_pool_t *pool);
-
-
-
-
-
 /* Declare all the command procedures */
 svn_cl__cmd_proc_t
   svn_cl__add,
@@ -125,7 +105,6 @@
   svn_cl__copy,
   svn_cl__delete,
   svn_cl__diff,
-  svn_cl__help,
   svn_cl__import,
   svn_cl__log,
   svn_cl__merge,
@@ -143,11 +122,6 @@
   svn_cl__update;
 
 
-/* Print a generic (non-command-specific) usage message. */
-void
-svn_cl__print_generic_help (apr_pool_t *pool, FILE *stream);
-
-
 /* Print out commit information found in COMMIT_INFO to the console. */
 void
 svn_cl__print_commit_info (svn_client_commit_info_t *commit_info);
@@ -155,25 +129,10 @@
 
 /*** Miscellaneous utility commands ***/
 
-/* Look up CODE in OPTION_TABLE.   If any option in the table has this
-   enum code, return a pointer to the option.  Else return NULL. */
-const apr_getopt_option_t *
-svn_cl__get_option_from_enum (int code,
-                              const apr_getopt_option_t *option_table);
-
-
 void svn_cl__push_svn_string (apr_array_header_t *array,
                               const char *str,
                               apr_pool_t *pool);
 
-/* Subcommands call this to pull any args left into the array of targets.
-   This includes any extra args passed in the file specified by
-   --targets.  */
-apr_array_header_t*
-svn_cl__args_to_target_array (apr_getopt_t *os,
-			      svn_cl__opt_state_t *opt_state,
-                              apr_pool_t *pool);
-
 /* Splits a list of whitespace-separated values into an apr_array_header_t */
 apr_array_header_t*
 svn_cl__stringlist_to_array (svn_stringbuf_t *buffer, apr_pool_t *pool);
@@ -185,23 +144,6 @@
 void svn_cl__push_implicit_dot_target (apr_array_header_t *targets,
                                        apr_pool_t *pool);
 
-svn_error_t *
-svn_cl__parse_num_args (apr_getopt_t *os,
-                        svn_cl__opt_state_t *opt_state,
-                        const char *subcommand,
-                        int num_args,
-                        apr_pool_t *pool);
-
-svn_error_t *
-svn_cl__parse_all_args (apr_getopt_t *os,
-                        svn_cl__opt_state_t *opt_state,
-                        const char *subcommand,
-                        apr_pool_t *pool);
-
-void
-svn_cl__subcommand_help (const char *subcommand,
-                         apr_pool_t *pool);
-
 
 /*** Command-line output functions -- printing to the user. ***/
 
@@ -342,4 +284,3 @@
  * eval: (load-file "../../../tools/dev/svn-dev.el")
  * end: 
  */
-
Index: ./subversion/clients/cmdline/checkout-cmd.c
===================================================================
--- ./subversion/clients/cmdline/checkout-cmd.c
+++ ./subversion/clients/cmdline/checkout-cmd.c	Fri Mar  8 09:11:18 2002
@@ -34,16 +34,18 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__checkout (apr_getopt_t *os,
-                  svn_cl__opt_state_t *opt_state,
+svn_cl__checkout (svn_getopt_t *os,
+                  void *opt_state_,
                   apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
+  apr_array_header_t *args;
   const svn_delta_editor_t *trace_editor;
   void *trace_edit_baton;
   int i;
   svn_client_auth_baton_t *auth_baton;
   
-  SVN_ERR (svn_cl__parse_all_args (os, opt_state, "checkout", pool));
+  SVN_ERR (svn_cl__parse_all_args (&args, os, "checkout", pool));
   
   /* Put commandline auth info into a baton for libsvn_client.  */
   auth_baton = svn_cl__make_auth_baton (opt_state, pool);
@@ -88,11 +90,11 @@
     (args->nelts == 1) or (args->nelts > 1). -Fitz
 
    */
-  for (i = 0; i < opt_state->args->nelts; i++)
+  for (i = 0; i < args->nelts; i++)
     {
       svn_stringbuf_t *local_dir;
       svn_stringbuf_t *repos_url
-        = ((svn_stringbuf_t **) (opt_state->args->elts))[0];
+        = ((svn_stringbuf_t **) (args->elts))[0];
 
       /* Canonicalize the URL. */
       /* ### um. this function isn't really designed for URLs... */
Index: ./subversion/clients/cmdline/propdel-cmd.c
===================================================================
--- ./subversion/clients/cmdline/propdel-cmd.c
+++ ./subversion/clients/cmdline/propdel-cmd.c	Thu Apr  4 17:10:06 2002
@@ -34,21 +34,23 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propdel (apr_getopt_t *os,
-                 svn_cl__opt_state_t *opt_state,
+svn_cl__propdel (svn_getopt_t *os,
+                 void *opt_state_,
                  apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
+  apr_array_header_t *args;
   svn_stringbuf_t *pname;
   apr_array_header_t *targets;
   int i;
 
-  SVN_ERR (svn_cl__parse_num_args (os, opt_state, "propdel", 1, pool));
+  SVN_ERR (svn_cl__parse_num_args (&args, os, "propdel", 1, pool));
 
   /* Get the property's name. */
-  pname = ((svn_stringbuf_t **) (opt_state->args->elts))[0];
+  pname = ((svn_stringbuf_t **) (args->elts))[0];
 
   /* Suck up all the remaining arguments into a targets array */
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Add "." if user passed 0 file arguments */
   svn_cl__push_implicit_dot_target (targets, pool);
Index: ./subversion/clients/cmdline/move-cmd.c
===================================================================
--- ./subversion/clients/cmdline/move-cmd.c
+++ ./subversion/clients/cmdline/move-cmd.c	Thu Apr  4 16:13:23 2002
@@ -35,20 +35,21 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__move (apr_getopt_t *os,
-              svn_cl__opt_state_t *opt_state,
+svn_cl__move (svn_getopt_t *os,
+              void *opt_state_,
               apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   svn_stringbuf_t *src_path, *dst_path;
   svn_client_auth_baton_t *auth_baton = NULL;
   svn_client_commit_info_t *commit_info = NULL;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   if (targets->nelts != 2)
     {
-      svn_cl__subcommand_help ("move", pool);
+      svn_cl__print_subcommand_help ("move", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
 
Index: ./subversion/clients/cmdline/mkdir-cmd.c
===================================================================
--- ./subversion/clients/cmdline/mkdir-cmd.c
+++ ./subversion/clients/cmdline/mkdir-cmd.c	Thu Apr  4 16:13:23 2002
@@ -35,16 +35,17 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__mkdir (apr_getopt_t *os,
-               svn_cl__opt_state_t *opt_state,
+svn_cl__mkdir (svn_getopt_t *os,
+               void *opt_state_,
                apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   svn_client_auth_baton_t *auth_baton = NULL;
   int i;
   svn_client_commit_info_t *commit_info = NULL;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Build an authentication object to give to libsvn_client. */
   auth_baton = svn_cl__make_auth_baton (opt_state, pool);
@@ -66,7 +67,7 @@
     }
   else
     {
-      svn_cl__subcommand_help ("mkdir", pool);
+      svn_cl__print_subcommand_help ("mkdir", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
 
Index: ./subversion/clients/cmdline/revert-cmd.c
===================================================================
--- ./subversion/clients/cmdline/revert-cmd.c
+++ ./subversion/clients/cmdline/revert-cmd.c	Thu Mar 14 16:24:20 2002
@@ -33,15 +33,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__revert (apr_getopt_t *os,
-                svn_cl__opt_state_t *opt_state,
+svn_cl__revert (svn_getopt_t *os,
+                void *opt_state_,
                 apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   int i;
   svn_boolean_t recursive = opt_state->recursive;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Revert has no implicit dot-target `.', so don't you put that code
      here! */
@@ -59,7 +60,7 @@
       }
   else
     {
-      svn_cl__subcommand_help ("revert", pool);
+      svn_cl__print_subcommand_help ("revert", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
   
Index: ./subversion/clients/cmdline/diff-cmd.c
===================================================================
--- ./subversion/clients/cmdline/diff-cmd.c
+++ ./subversion/clients/cmdline/diff-cmd.c	Tue Mar 12 18:08:15 2002
@@ -36,10 +36,11 @@
 
 /* An svn_cl__cmd_proc_t to handle the 'diff' command. */
 svn_error_t *
-svn_cl__diff (apr_getopt_t *os,
-              svn_cl__opt_state_t *opt_state,
+svn_cl__diff (svn_getopt_t *os,
+              void *opt_state_,
               apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *options;
   apr_array_header_t *targets;
   apr_array_header_t *condensed_targets;
@@ -50,7 +51,7 @@
 
   options = svn_cl__stringlist_to_array (opt_state->extensions, pool);
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
   svn_cl__push_implicit_dot_target (targets, pool);
   SVN_ERR (svn_path_remove_redundancies (&condensed_targets,
                                          targets,
Index: ./subversion/clients/cmdline/copy-cmd.c
===================================================================
--- ./subversion/clients/cmdline/copy-cmd.c
+++ ./subversion/clients/cmdline/copy-cmd.c	Thu Apr  4 16:52:24 2002
@@ -35,10 +35,11 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__copy (apr_getopt_t *os,
-              svn_cl__opt_state_t *opt_state,
+svn_cl__copy (svn_getopt_t *os,
+              void *opt_state_,
               apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   svn_stringbuf_t *src_path, *dst_path;
   svn_string_t path_str;
@@ -48,10 +49,10 @@
   svn_boolean_t src_is_url, dst_is_url;
   svn_client_commit_info_t *commit_info = NULL;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
   if (targets->nelts != 2)
     {
-      svn_cl__subcommand_help ("copy", pool);
+      svn_cl__print_subcommand_help ("copy", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
 
Index: ./subversion/clients/cmdline/util.c
===================================================================
--- ./subversion/clients/cmdline/util.c
+++ ./subversion/clients/cmdline/util.c	Thu Apr  4 16:13:24 2002
@@ -45,18 +45,6 @@
 
 #define DEFAULT_ARRAY_SIZE 5
 
-/* Hmm. This should probably find its way into libsvn_subr -Fitz */
-/* Create a SVN string from the char* and add it to the array */
-static void 
-array_push_svn_stringbuf (apr_array_header_t *array,
-                          const char *str,
-                          apr_pool_t *pool)
-{
-  (*((svn_stringbuf_t **) apr_array_push (array)))
-    = svn_stringbuf_create (str, pool);
-}
-
-
 /* Some commands take an implicit "." string argument when invoked
  * with no arguments. Those commands make use of this function to
  * add "." to the target array if the user passes no args */
@@ -69,112 +57,6 @@
   assert (targets->nelts);
 }
 
-/* Parse a given number of non-target arguments from the
- * command line args passed in by the user. Put them
- * into the opt_state args array */
-svn_error_t *
-svn_cl__parse_num_args (apr_getopt_t *os,
-                        svn_cl__opt_state_t *opt_state,
-                        const char *subcommand,
-                        int num_args,
-                        apr_pool_t *pool)
-{
-  int i;
-  
-  opt_state->args = apr_array_make (pool, DEFAULT_ARRAY_SIZE, 
-                                    sizeof (svn_stringbuf_t *));
-
-  /* loop for num_args and add each arg to the args array */
-  for (i = 0; i < num_args; i++)
-    {
-      if (os->ind >= os->argc)
-        {
-          svn_cl__subcommand_help (subcommand, pool);
-          return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 
-                                   0, 0, pool, "");
-        }
-      array_push_svn_stringbuf (opt_state->args, os->argv[os->ind++], pool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Parse all of the arguments from the command line args
- * passed in by the user. Put them into the opt_state
- * args array */
-svn_error_t *
-svn_cl__parse_all_args (apr_getopt_t *os,
-                        svn_cl__opt_state_t *opt_state,
-                        const char *subcommand,
-                        apr_pool_t *pool)
-{
-  opt_state->args = apr_array_make (pool, DEFAULT_ARRAY_SIZE, 
-                                    sizeof (svn_stringbuf_t *));
-
-  if (os->ind >= os->argc)
-    {
-      svn_cl__subcommand_help (subcommand, pool);
-      return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
-    }
-
-  while (os->ind < os->argc)
-    {
-      array_push_svn_stringbuf (opt_state->args, os->argv[os->ind++], pool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Create a targets array and add all the remaining arguments
- * to it. We also process arguments passed in the --target file, if
- * specified, just as if they were passed on the command line.  */
-apr_array_header_t*
-svn_cl__args_to_target_array (apr_getopt_t *os,
-			      svn_cl__opt_state_t *opt_state,
-                              apr_pool_t *pool)
-{
-  apr_array_header_t *targets =
-    apr_array_make (pool, DEFAULT_ARRAY_SIZE, sizeof (svn_stringbuf_t *));
- 
-  /* Command line args take precendence.  */
-  for (; os->ind < os->argc; os->ind++)
-    {
-      svn_stringbuf_t *target = svn_stringbuf_create (os->argv[os->ind], pool);
-      svn_string_t tstr;
-
-      /* If this path looks like it would work as a URL in one of the
-         currently available RA libraries, we add it unconditionally
-         to the target array. */
-      tstr.data = target->data;
-      tstr.len  = target->len;
-      if (! svn_path_is_url (&tstr))
-        {
-          const char *basename = svn_path_basename (target->data, pool);
-
-          /* If this target is a Subversion administrative directory,
-             skip it.  TODO: Perhaps this check should not call the
-             target a SVN admin dir unless svn_wc_check_wc passes on
-             the target, too? */
-          if (! strcmp (basename, SVN_WC_ADM_DIR_NAME))
-            continue;
-        }
-      else
-        {
-          svn_path_canonicalize (target);
-        }
-      (*((svn_stringbuf_t **) apr_array_push (targets))) = target;
-    }
-
-  /* Now args from --targets, if any */
-  if (NULL != opt_state->targets)
-    apr_array_cat(targets, opt_state->targets);
-
-  /* kff todo: need to remove redundancies from targets before
-     passing it to the cmd_func. */
-     
-  return targets;
-}
-
 /* Convert a whitespace separated list of items into an apr_array_header_t */
 apr_array_header_t*
 svn_cl__stringlist_to_array(svn_stringbuf_t *buffer, apr_pool_t *pool)
Index: ./subversion/clients/cmdline/propget-cmd.c
===================================================================
--- ./subversion/clients/cmdline/propget-cmd.c
+++ ./subversion/clients/cmdline/propget-cmd.c	Thu Mar 14 16:23:41 2002
@@ -34,22 +34,23 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propget (apr_getopt_t *os,
-                 svn_cl__opt_state_t *opt_state,
+svn_cl__propget (svn_getopt_t *os,
+                 void *opt_state_,
                  apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
+  apr_array_header_t *args;
   svn_stringbuf_t *propname;
   apr_array_header_t *targets;
   int i;
 
   /* PROPNAME is first argument */
-  SVN_ERR (svn_cl__parse_num_args (os, opt_state,
-                                   "propget", 1, pool));
+  SVN_ERR (svn_cl__parse_num_args (&args, os, "propget", 1, pool));
 
-  propname = ((svn_stringbuf_t **) (opt_state->args->elts))[0];
+  propname = ((svn_stringbuf_t **) (args->elts))[0];
 
   /* suck up all the remaining arguments into a targets array */
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Add "." if user passed 0 file arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: ./subversion/clients/cmdline/log-cmd.c
===================================================================
--- ./subversion/clients/cmdline/log-cmd.c
+++ ./subversion/clients/cmdline/log-cmd.c	Tue Mar 12 18:08:48 2002
@@ -166,15 +166,16 @@
 
 
 svn_error_t *
-svn_cl__log (apr_getopt_t *os,
-             svn_cl__opt_state_t *opt_state,
+svn_cl__log (svn_getopt_t *os,
+             void *opt_state_,
              apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   svn_client_auth_baton_t *auth_baton;
   struct log_message_receiver_baton lb;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Build an authentication object to give to libsvn_client. */
   auth_baton = svn_cl__make_auth_baton (opt_state, pool);
Index: ./subversion/clients/cmdline/update-cmd.c
===================================================================
--- ./subversion/clients/cmdline/update-cmd.c
+++ ./subversion/clients/cmdline/update-cmd.c	Thu Mar 14 16:25:20 2002
@@ -34,16 +34,17 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__update (apr_getopt_t *os,
-                svn_cl__opt_state_t *opt_state,
+svn_cl__update (svn_getopt_t *os,
+                void *opt_state_,
                 apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   apr_array_header_t *condensed_targets;
   int i;
   svn_client_auth_baton_t *auth_baton;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Build an authentication baton to give to libsvn_client. */
   auth_baton = svn_cl__make_auth_baton (opt_state, pool);
Index: ./subversion/clients/cmdline/cleanup-cmd.c
===================================================================
--- ./subversion/clients/cmdline/cleanup-cmd.c
+++ ./subversion/clients/cmdline/cleanup-cmd.c	Tue Mar 12 18:06:36 2002
@@ -33,14 +33,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__cleanup (apr_getopt_t *os,
-                 svn_cl__opt_state_t *opt_state,
+svn_cl__cleanup (svn_getopt_t *os,
+                 void *opt_state_,
                  apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   int i;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
@@ -54,7 +55,7 @@
       }
   else
     {
-      svn_cl__subcommand_help ("cleanup", pool);
+      svn_cl__print_subcommand_help ("cleanup", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
 
Index: ./subversion/clients/cmdline/add-cmd.c
===================================================================
--- ./subversion/clients/cmdline/add-cmd.c
+++ ./subversion/clients/cmdline/add-cmd.c	Tue Mar 12 18:07:20 2002
@@ -36,16 +36,17 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__add (apr_getopt_t *os,
-             svn_cl__opt_state_t *opt_state,
+svn_cl__add (svn_getopt_t *os,
+             void *opt_state_,
              apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   svn_error_t *err;
   apr_array_header_t *targets;
   int i;
   svn_boolean_t recursive = opt_state->recursive;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   if (targets->nelts)
     {
@@ -76,7 +77,7 @@
     }
   else
     {
-      svn_cl__subcommand_help ("add", pool);
+      svn_cl__print_subcommand_help ("add", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
 
Index: ./subversion/clients/cmdline/help-cmd.c
===================================================================
--- ./subversion/clients/cmdline/help-cmd.c
+++ ./subversion/clients/cmdline/.svn/empty-file	Fri Mar 15 10:09:05 2002
@@ -1,103 +0,0 @@
-/*
- * help-cmd.c -- Provide help
- *
- * ====================================================================
- * Copyright (c) 2000-2002 CollabNet.  All rights reserved.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution.  The terms
- * are also available at http://subversion.tigris.org/license-1.html.
- * If newer versions of this license are posted there, you may use a
- * newer version instead, at your option.
- *
- * This software consists of voluntary contributions made by many
- * individuals.  For exact contribution history, see the revision
- * history and logs, available at http://subversion.tigris.org/.
- * ====================================================================
- */
-
-/* ==================================================================== */
-
-
-
-/*** Includes. ***/
-
-#include "svn_client.h"
-#include "svn_string.h"
-#include "svn_error.h"
-#include "svn_version.h"
-#include "cl.h"
-
-
-/*** Code. ***/
-
-static svn_error_t *
-print_version_info (apr_pool_t *pool)
-{
-  void *ra_baton;
-  svn_stringbuf_t *descriptions;
-  static const char info[] =
-    "Copyright (C) 2000-2002 CollabNet.\n"
-    "Subversion is open source software, see http://subversion.tigris.org/\n";
-
-  printf ("Subversion Client, version %s\n", SVN_VERSION);
-  printf ("   compiled %s, %s\n\n", __DATE__, __TIME__);
-  printf ("%s\n", info);
-
-  printf ("The following repository access (RA) modules are available:\n\n");
-
-  /* Get a hash full of all available RA libraries.  */
-  SVN_ERR (svn_ra_init_ra_libs (&ra_baton, pool));
-
-  /* Get a descriptive list of them. */
-  SVN_ERR (svn_ra_print_ra_libraries (&descriptions, ra_baton, pool));
-
-  printf ("%s\n", descriptions->data);
-
-  return SVN_NO_ERROR;
-}
-
-
-
-/* Print either generic help, or command-specific help for each
- * command in os->args.  OPT_STATE is only examined for the
- * '--version' switch.  If OS is null then generic help will always be
- * printed.
- * 
- * Unlike all the other command routines, ``help'' has its own
- * option processing.
- */
-svn_error_t *
-svn_cl__help (apr_getopt_t *os,
-              svn_cl__opt_state_t *opt_state,
-              apr_pool_t *pool)
-{
-  apr_array_header_t *targets = NULL;
-  int i;
-
-  if (os)
-    targets = svn_cl__args_to_target_array (os, opt_state, pool);
-
-  if (targets && targets->nelts)  /* help on subcommand(s) requested */
-    for (i = 0; i < targets->nelts; i++)
-      {
-        svn_stringbuf_t *this = (((svn_stringbuf_t **) (targets)->elts))[i];
-	svn_cl__subcommand_help (this->data, pool);
-      }
-  else if (opt_state && opt_state->version)  /* just -v or --version */
-    SVN_ERR (print_version_info (pool));        
-  else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
-    svn_cl__print_generic_help (pool, stdout);  
-  else                                       /* unknown option or cmd */
-    svn_cl__print_generic_help (pool, stderr);
-
-  return SVN_NO_ERROR;
-}
-
-
-
-/* 
- * local variables:
- * eval: (load-file "../../../tools/dev/svn-dev.el")
- * end: 
- */
Index: ./subversion/clients/cmdline/commit-cmd.c
===================================================================
--- ./subversion/clients/cmdline/commit-cmd.c
+++ ./subversion/clients/cmdline/commit-cmd.c	Thu Apr  4 16:50:40 2002
@@ -40,10 +40,11 @@
 
 
 svn_error_t *
-svn_cl__commit (apr_getopt_t *os,
-                svn_cl__opt_state_t *opt_state,
+svn_cl__commit (svn_getopt_t *os,
+                void *opt_state_,
                 apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   apr_array_header_t *condensed_targets;
   svn_stringbuf_t *base_dir;
@@ -51,7 +52,7 @@
   svn_client_commit_info_t *commit_info = NULL;
   svn_revnum_t revnum;
     
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Build an authentication object to give to libsvn_client. */
   auth_baton = svn_cl__make_auth_baton (opt_state, pool);
Index: ./subversion/clients/cmdline/propset-cmd.c
===================================================================
--- ./subversion/clients/cmdline/propset-cmd.c
+++ ./subversion/clients/cmdline/propset-cmd.c	Thu Mar 14 16:24:10 2002
@@ -34,10 +34,12 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propset (apr_getopt_t *os,
-                 svn_cl__opt_state_t *opt_state,
+svn_cl__propset (svn_getopt_t *os,
+                 void *opt_state_,
                  apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
+  apr_array_header_t *args;
   svn_stringbuf_t *propname;
   const svn_string_t *propval = NULL;
   apr_array_header_t *targets;
@@ -50,18 +52,18 @@
   }
   /* PROPNAME and PROPVAL expected as first 2 arguments if filedata
      was NULL */
-  SVN_ERR (svn_cl__parse_num_args (os, opt_state,
-                                   "propset", num_args_wanted, pool));
+  SVN_ERR (svn_cl__parse_num_args (&args, os, "propset",
+                                   num_args_wanted, pool));
 
-  propname  = ((svn_stringbuf_t **) (opt_state->args->elts))[0];
+  propname  = ((svn_stringbuf_t **) (args->elts))[0];
   if (num_args_wanted == 2)
     {
-      svn_stringbuf_t *buf = ((svn_stringbuf_t **) (opt_state->args->elts))[1];
+      svn_stringbuf_t *buf = ((svn_stringbuf_t **) (args->elts))[1];
       propval = svn_string_create_from_buf (buf, pool);
     }
 
   /* suck up all the remaining arguments into a targets array */
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Add "." if user passed 0 file arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: ./subversion/clients/cmdline/switch-cmd.c
===================================================================
--- ./subversion/clients/cmdline/switch-cmd.c
+++ ./subversion/clients/cmdline/switch-cmd.c	Thu Mar 14 16:25:07 2002
@@ -35,10 +35,11 @@
 
 
 svn_error_t *
-svn_cl__switch (apr_getopt_t *os,
-                svn_cl__opt_state_t *opt_state,
+svn_cl__switch (svn_getopt_t *os,
+                void *opt_state_,
                 apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   svn_stringbuf_t *target = NULL, *switch_url = NULL;
   svn_string_t str;
@@ -59,10 +60,10 @@
   /* This command should discover (or derive) exactly two cmdline
      arguments: a local path to update ("target"), and a new url to
      switch to ("switch_url"). */
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
   if (targets->nelts == 0)
     {
-      svn_cl__subcommand_help ("switch", pool);
+      svn_cl__print_subcommand_help ("switch", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
   if (targets->nelts == 1)
Index: ./subversion/clients/cmdline/delete-cmd.c
===================================================================
--- ./subversion/clients/cmdline/delete-cmd.c
+++ ./subversion/clients/cmdline/delete-cmd.c	Thu Apr  4 16:13:24 2002
@@ -35,16 +35,17 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__delete (apr_getopt_t *os,
-                svn_cl__opt_state_t *opt_state,
+svn_cl__delete (svn_getopt_t *os,
+                void *opt_state_,
                 apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   svn_client_auth_baton_t *auth_baton = NULL;
   int i;
   svn_client_commit_info_t *commit_info = NULL;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Build an authentication object to give to libsvn_client. */
   auth_baton = svn_cl__make_auth_baton (opt_state, pool);
@@ -67,7 +68,7 @@
     }
   else
     {
-      svn_cl__subcommand_help ("delete", pool);
+      svn_cl__print_subcommand_help ("delete", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
 
Index: ./subversion/clients/cmdline/import-cmd.c
===================================================================
--- ./subversion/clients/cmdline/import-cmd.c
+++ ./subversion/clients/cmdline/import-cmd.c	Thu Apr  4 16:13:24 2002
@@ -34,10 +34,11 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__import (apr_getopt_t *os,
-                svn_cl__opt_state_t *opt_state,
+svn_cl__import (svn_getopt_t *os,
+                void *opt_state_,
                 apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   svn_stringbuf_t *path;
   svn_stringbuf_t *url;
@@ -81,7 +82,7 @@
    * ### kff todo: review above behaviors.
    */
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Get a repository url. */
   if (targets->nelts < 1)
Index: ./subversion/clients/cmdline/proplist-cmd.c
===================================================================
--- ./subversion/clients/cmdline/proplist-cmd.c
+++ ./subversion/clients/cmdline/proplist-cmd.c	Thu Mar 14 16:23:51 2002
@@ -34,14 +34,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__proplist (apr_getopt_t *os,
-                  svn_cl__opt_state_t *opt_state,
+svn_cl__proplist (svn_getopt_t *os,
+                  void *opt_state_,
                   apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_array_header_t *targets;
   int i;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: ./subversion/clients/cmdline/resolve-cmd.c
===================================================================
--- ./subversion/clients/cmdline/resolve-cmd.c
+++ ./subversion/clients/cmdline/resolve-cmd.c	Thu Mar 14 16:35:03 2002
@@ -36,15 +36,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__resolve (apr_getopt_t *os,
-                 svn_cl__opt_state_t *opt_state,
+svn_cl__resolve (svn_getopt_t *os,
+                 void *opt_state_,
                  apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   svn_error_t *err;
   apr_array_header_t *targets;
   int i;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   if (targets->nelts)
     {
@@ -70,7 +71,7 @@
     }
   else
     {
-      svn_cl__subcommand_help ("resolve", pool);
+      svn_cl__print_subcommand_help ("resolve", os, pool);
       return svn_error_create (SVN_ERR_CL_ARG_PARSING_ERROR, 0, 0, pool, "");
     }
 
Index: ./subversion/clients/cmdline/status-cmd.c
===================================================================
--- ./subversion/clients/cmdline/status-cmd.c
+++ ./subversion/clients/cmdline/status-cmd.c	Thu Mar 14 16:24:56 2002
@@ -34,17 +34,18 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__status (apr_getopt_t *os,
-                svn_cl__opt_state_t *opt_state,
+svn_cl__status (svn_getopt_t *os,
+                void *opt_state_,
                 apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
   apr_hash_t *statushash;
   apr_array_header_t *targets;
   int i;
   svn_client_auth_baton_t *auth_baton;
   svn_revnum_t youngest = SVN_INVALID_REVNUM;
 
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Build an authentication object to give to libsvn_client. */
   auth_baton = svn_cl__make_auth_baton (opt_state, pool);
Index: ./subversion/clients/cmdline/main.c
===================================================================
--- ./subversion/clients/cmdline/main.c
+++ ./subversion/clients/cmdline/main.c	Wed Mar 20 17:01:23 2002
@@ -72,41 +72,9 @@
     {0,               0, 0}
   };
 
-
-/* The maximum number of options that can be accepted by a subcommand;
-   this is simply the number of unique switches that exist in the
-   table above.  */
-#define SVN_CL__MAX_OPTS sizeof(svn_cl__options)/sizeof(svn_cl__options[0])
-
-
 
 /*** Command dispatch. ***/
 
-/* The maximum number of aliases a subcommand can have. */
-#define SVN_CL__MAX_ALIASES 3
-
-
-/* One element of the command dispatch table. */
-typedef struct svn_cl__cmd_desc_t
-{
-  /* The full name of this command. */
-  const char *name;
-
-  /* The function this command invokes. */
-  svn_cl__cmd_proc_t *cmd_func;
-
-  /* A list of alias names for this command. */
-  const char *aliases[SVN_CL__MAX_ALIASES];
-
-  /* A brief string describing this command, for usage messages. */
-  const char *help;
-
-  /* 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_CL__MAX_OPTS];
-
-} svn_cl__cmd_desc_t;
-
 
 
 /* Our array of available subcommands.
@@ -119,14 +87,14 @@
     "Put files and directories under revision control, scheduling\n"
     "them for addition to repository.  They will be added in next commit.\n"
     "usage: svn add [OPTIONS] [TARGETS]\n", 
-    {svn_cl__targets_opt, svn_cl__recursive_opt} },
+    {svn_cl__recursive_opt, svn_cl__recursive_opt, 0} },
 
-  { "checkout", svn_cl__checkout, {"co"},
+  { "checkout", svn_cl__checkout, {"co", 0},
     "Check out a working copy from a repository.\n"
     "usage: svn checkout REPOS_URL\n",    
     {'d', 'r', 'D', 'q', 'n',
      svn_cl__auth_username_opt, svn_cl__auth_password_opt,
-     svn_cl__xml_file_opt }  },
+     svn_cl__xml_file_opt, 0} },
 
   { "cleanup", svn_cl__cleanup, {0},
     "Recursively clean up the working copy, removing locks, resuming\n"
@@ -134,16 +102,16 @@
     "usage: svn cleanup [TARGETS]\n",
     {0} },
   
-  { "commit", svn_cl__commit, {"ci"},
+  { "commit", svn_cl__commit, {"ci", 0},
     "Send changes from your working copy to the repository.\n"
     "usage: svn commit [TARGETS]\n\n"
     "   Be sure to use one of -m or -F to send a log message;\n"
     "   the -r switch is only for use with --xml-file.\n",
     {'m', 'F', 'q', svn_cl__targets_opt,
      svn_cl__force_opt, svn_cl__auth_username_opt, svn_cl__auth_password_opt,
-     svn_cl__xml_file_opt, 'r'} },
+     svn_cl__xml_file_opt, 'r', 0} },
   
-  { "copy", svn_cl__copy, {"cp"},
+  { "copy", svn_cl__copy, {"cp", 0},
     "Duplicate something in working copy or repos, remembering history.\n"
     "usage: svn copy SRC DST.\n\n"
     "  SRC and DST can each be either a working copy (WC) path or URL:\n"
@@ -151,9 +119,9 @@
     "    WC  -> URL:  immediately commit a copy of WC to URL\n"
     "    URL -> WC:   check out URL into WC, schedule for addition\n"
     "    URL -> URL:  complete server-side copy;  used to branch & tag\n",
-    {'m', 'F', 'r', svn_cl__auth_username_opt, svn_cl__auth_password_opt} },
+    {'m', 'F', 'r', svn_cl__auth_username_opt, svn_cl__auth_password_opt, 0} },
   
-  { "delete", svn_cl__delete, {"del", "remove", "rm"},
+  { "delete", svn_cl__delete, {"del", "remove", "rm", 0},
     "Remove files and directories from version control.\n"
     "usage: svn delete [TARGET | URL]\n\n"
     "    If run on a working-copy TARGET, item is scheduled for deletion\n"
@@ -161,19 +129,19 @@
     "    if --force is passed.)  If run on URL, item is deleted from\n"
     "    repository via an immediate commit.\n",
     {svn_cl__force_opt, 'm', 'F', svn_cl__targets_opt,
-     svn_cl__auth_username_opt, svn_cl__auth_password_opt} },
+     svn_cl__auth_username_opt, svn_cl__auth_password_opt, 0} },
   
-  { "diff", svn_cl__diff, {"di"},
+  { "diff", svn_cl__diff, {"di", 0},
     "Display local changes in the working copy, or changes between the\n"
     "working copy and the repository if a revision is given.\n"
     "usage: svn diff [-r REV1[:REV2]] [TARGETS]\n",
     {'r', 'D', 'x', 'n',
-     svn_cl__auth_username_opt, svn_cl__auth_password_opt} },
-  
-  { "help", svn_cl__help, {"?", "h"},
+     svn_cl__auth_username_opt, svn_cl__auth_password_opt, 0} },
+
+  { "help", svn_cl__help, {"?", "h", 0},
     "Display this usage message.\n"
     "usage: svn help [SUBCOMMAND1 [SUBCOMMAND2] ...]\n",
-    {svn_cl__version_opt} },
+    {svn_cl__version_opt, 0} },
   /* We need to support "--help", "-?", and all that good stuff, of
      course.  But those options, since unknown, will result in the
      help message being printed out anyway, so there's no need to
@@ -187,7 +155,7 @@
     "    directly.  Otherwise, create NEW_ENTRY underneath REPOS_URL and\n"
     "    begin copy there.  (-r is only needed if importing to --xml-file)\n",
     {'F', 'm', 'q', svn_cl__auth_username_opt, svn_cl__auth_password_opt,
-     svn_cl__xml_file_opt, 'r'} },
+     svn_cl__xml_file_opt, 'r', 0} },
   
   { "log", svn_cl__log, {0},
     "Show the log messages for a set of revision(s) and/or file(s).\n"
@@ -205,62 +173,62 @@
     "\n"
     "    svn log http://www.example.com/repo/project foo.c bar.c\n",
     {'r', 'D', 'v', svn_cl__targets_opt, svn_cl__auth_username_opt,
-     svn_cl__auth_password_opt} },
+     svn_cl__auth_password_opt, 0} },
   
   { "merge", svn_cl__merge, {0},
     "Merge changes in the working copy.  IMPLEMENTATION INCOMPLETE.\n"
     "usage: svn merge [-r REV1[:REV2]] [TARGETS]\n",
     {'r', 'D', 'x', 'n',
-     svn_cl__auth_username_opt, svn_cl__auth_password_opt} },
+     svn_cl__auth_username_opt, svn_cl__auth_password_opt, 0} },
   
   { "mkdir", svn_cl__mkdir, {0},
     "Create a new directory under revision control.\n"
     "usage: mkdir [NEW_DIR | REPOS_URL].\n\n"
     "    Either create NEW_DIR in working copy scheduled for addition,\n"
     "    or create REPOS_URL via immediate commit.\n",
-    {'m', 'F', svn_cl__auth_username_opt, svn_cl__auth_password_opt} },
+    {'m', 'F', svn_cl__auth_username_opt, svn_cl__auth_password_opt, 0} },
 
-  { "move", svn_cl__move, {"mv", "rename", "ren"},
+  { "move", svn_cl__move, {"mv", "rename", "ren", 0},
     "Move/rename something in working copy or repository.\n"
     "usage: move SRC DST.\n\n"
     "  NOTE:  this command is equivalent to a 'copy' and 'delete'.\n\n"
     "  SRC and DST can both be working copy (WC) paths or URLs:\n"
     "    WC  -> WC:   move and schedule for addition (with history)\n"
     "    URL -> URL:  complete server-side rename.\n",    
-    {'m', 'F', 'r', svn_cl__auth_username_opt, svn_cl__auth_password_opt} },
+    {'m', 'F', 'r', svn_cl__auth_username_opt, svn_cl__auth_password_opt, 0} },
   
-  { "propdel", svn_cl__propdel, {"pdel"},
+  { "propdel", svn_cl__propdel, {"pdel", 0},
     "Remove property PROPNAME on files and directories.\n"
     "usage: propdel PROPNAME [TARGETS]\n",
-    {'q', svn_cl__recursive_opt} },
+    {'q', svn_cl__recursive_opt, 0} },
   
-  { "propedit", svn_cl__propedit, {"pedit", "pe"},
+  { "propedit", svn_cl__propedit, {"pedit", "pe", 0},
     "Edit property PROPNAME with $EDITOR on targets.\n"
     "usage: propedit PROPNAME [TARGETS]\n",
     {0} },
   
-  { "propget", svn_cl__propget, {"pget", "pg"},
+  { "propget", svn_cl__propget, {"pget", "pg", 0},
     "Print value of property PROPNAME on files or directories.\n"
     "usage: propget PROPNAME [TARGETS]\n",
-    {svn_cl__recursive_opt} },
+    {svn_cl__recursive_opt, 0} },
   
-  { "proplist", svn_cl__proplist, {"plist", "pl"},
+  { "proplist", svn_cl__proplist, {"plist", "pl", 0},
     "List all properties attached to files or directories.\n"
     "usage: proplist [TARGETS]\n",
-    {'v', svn_cl__recursive_opt} },
+    {'v', svn_cl__recursive_opt, 0} },
   
-  { "propset", svn_cl__propset, {"pset", "ps"},
+  { "propset", svn_cl__propset, {"pset", "ps", 0},
     "Set property PROPNAME to PROPVAL on files or directories.\n"
     "usage: propset PROPNAME PROPVAL [TARGETS]\n\n"
     "    Use -F (instead of PROPVAL) to get the value from a file.\n",
-    {'F', 'q', svn_cl__targets_opt, svn_cl__recursive_opt} },
+    {'F', 'q', svn_cl__targets_opt, svn_cl__recursive_opt, 0} },
   
   { "revert", svn_cl__revert, {0},
     "Restore pristine working copy file (undo all local edits)\n"
     "usage: revert TARGET1 [TARGET2 [TARGET3 ... ]]\n\n"
-    "    Note:  this routine does not require network access, and \n"
+    "    Note:  this routine does not require network access, and\n"
     "    resolves any conflicted states.\n",
-    {svn_cl__targets_opt, svn_cl__recursive_opt} },
+    {svn_cl__targets_opt, svn_cl__recursive_opt, 0} },
 
   { "resolve", svn_cl__resolve, {0},
     "Remove 'conflicted' state on working copy files or directories.\n"
@@ -268,9 +236,9 @@
     "    Note:  this routine does not semantically resolve conflict markers;\n"
     "    it merely removes conflict-related artifact files and allows TARGET\n"
     "    to be committed again.\n",
-    {svn_cl__targets_opt} },
-  
-  { "status", svn_cl__status, {"stat", "st"},
+    {svn_cl__targets_opt, 0} },
+
+  { "status", svn_cl__status, {"stat", "st", 0},
     "Print the status of working copy files and directories.\n"
     "usage: svn status [TARGETS]\n\n"
     "   With no args, print only locally modified files (no network access).\n"
@@ -282,256 +250,28 @@
     "    _      *             965       970    sussman      ./build.conf\n"
     "    M                    965       687        joe      ./buildcheck.sh\n",
     { 'u', 'v', 'n', 'q',
-      svn_cl__auth_username_opt, svn_cl__auth_password_opt } },
+      svn_cl__auth_username_opt, svn_cl__auth_password_opt, 0} },
   
-  { "switch", svn_cl__switch, {"sw"},
+  { "switch", svn_cl__switch, {"sw", 0},
     "Update working copy to mirror a new URL\n"
     "usage: switch [TARGET] REPOS_URL\n\n" /* ### should args be reversed? */
     "   Note:  this is the way to move a working copy to a new branch.\n",
-    {'r', 'n', svn_cl__force_opt} },
+    {'r', 'n', svn_cl__force_opt, 0} },
  
-  { "update", svn_cl__update, {"up"}, 
+  { "update", svn_cl__update, {"up", 0}, 
     "Bring changes from the repository into the working copy.\n"
     "usage: update [TARGETS]\n\n"
     "  If no revision given, bring working copy up-to-date with HEAD rev.\n"
     "  Else synchronize working copy to revision given by -r or -D.\n",
     {'r', 'D', 'n', svn_cl__auth_username_opt,
-     svn_cl__auth_password_opt, svn_cl__xml_file_opt} },
+     svn_cl__auth_password_opt, svn_cl__xml_file_opt, 0} },
 
   { NULL, NULL, {0}, NULL, {0} }
 };
 
-
-
-
-/* Return the entry in svn_cl__cmd_table whose name matches CMD_NAME,
- * or NULL if none.  CMD_NAME may be an alias. */
-static const svn_cl__cmd_desc_t *
-svn_cl__get_canonical_command (const char *cmd_name)
-{
-  int i = 0;
-
-  if (cmd_name == NULL)
-    return NULL;
-
-  while (svn_cl__cmd_table[i].name) {
-    int j;
-    if (strcmp (cmd_name, svn_cl__cmd_table[i].name) == 0)
-      return svn_cl__cmd_table + i;
-    for (j = 0; 
-         (j < SVN_CL__MAX_ALIASES) && svn_cl__cmd_table[i].aliases[j]; 
-         j++)
-      if (strcmp (cmd_name, svn_cl__cmd_table[i].aliases[j]) == 0)
-        return svn_cl__cmd_table + i;
-
-    i++;
-  }
-
-  /* If we get here, there was no matching command name or alias. */
-  return NULL;
-}
-
-
-
 
 /*** 'help' processing ***/
 
-/* Print an option OPT nicely into a STRING allocated in POOL.  If DOC
-   is set, include generic documentation string of option.*/
-static void
-format_option (char **string,
-               const apr_getopt_option_t *opt,
-               svn_boolean_t doc,
-               apr_pool_t *pool)
-{
-  char *opts;
-
-  if (opt == NULL)
-    *string = apr_psprintf (pool, "?");
-
-  if (opt->optch <= 255)  
-    opts = apr_psprintf (pool, "-%c [--%s]", opt->optch, opt->name);
-  else
-    opts = apr_psprintf (pool, "--%s", opt->name);
-
-  if (opt->has_arg)
-    opts = apr_pstrcat (pool, opts, " arg", NULL);
-  
-  if (doc)
-    opts = apr_pstrcat (pool, opts, ":\t", opt->description, NULL);
-
-  *string = opts;
-}
-
-
-
-const apr_getopt_option_t *
-svn_cl__get_option_from_enum (int code,
-                              const apr_getopt_option_t *option_table)
-{
-  int i;
-  const apr_getopt_option_t *opt = NULL;
-
-  for (i = 0; i < SVN_CL__MAX_OPTS; i++)
-    {
-      if (option_table[i].optch == code)
-        {
-          opt = &(option_table[i]);
-          break;
-        }
-    }
-  
-  return opt;
-}
-
-
-/* Return TRUE iff subcommand COMMAND has OPTION_CODE listed within
-   it.  Else return FALSE. */
-static svn_boolean_t
-subcommand_takes_option (const svn_cl__cmd_desc_t *command,
-                         int option_code)
-{
-  int i;
-  
-  for (i = 0; i < SVN_CL__MAX_OPTS; i++)
-    {          
-      if (command->valid_options[i] == option_code)
-        return TRUE;
-    }
-  return FALSE;
-}
-
-
-/* Print the canonical command name for CMD, all its aliases,
-   and if HELP is set, print the help string for the command too. */
-static void
-print_command_info (const svn_cl__cmd_desc_t *cmd_desc,
-                    svn_boolean_t help, 
-                    apr_pool_t *pool,
-                    FILE *stream)
-{
-  const svn_cl__cmd_desc_t *canonical_cmd
-    = svn_cl__get_canonical_command (cmd_desc->name);
-  svn_boolean_t first_time;
-  int i;
-
-  /* Print the canonical command name. */
-  fputs (canonical_cmd->name, stream);
-
-  /* Print the list of aliases. */
-  first_time = TRUE;
-  for (i = 0; i < SVN_CL__MAX_ALIASES; i++) 
-    {
-      if (canonical_cmd->aliases[i] == NULL)
-        break;
-
-      if (first_time) {
-        fprintf (stream, " (");
-        first_time = FALSE;
-      }
-      else
-        fprintf (stream, ", ");
-      
-      fprintf (stream, "%s", canonical_cmd->aliases[i]);
-    }
-
-  if (! first_time)
-    fprintf (stream, ")");
-  
-  if (help)
-    {
-      const apr_getopt_option_t *option;
-      svn_boolean_t have_options = FALSE;
-
-      fprintf (stream, ": %s", canonical_cmd->help);
-
-      /* Loop over all valid option codes attached to the subcommand */
-      for (i = 0; i < SVN_CL__MAX_OPTS; i++)
-        {
-          if (canonical_cmd->valid_options[i])
-            {
-              if (have_options == FALSE)
-                {
-                  fprintf (stream, "\nValid options:\n");
-                  have_options = TRUE;
-                }
-
-              /* convert each option code into an option */
-              option = 
-                svn_cl__get_option_from_enum (canonical_cmd->valid_options[i],
-                                              svn_cl__options);
-
-              /* print the option's docstring */
-              if (option)
-                {
-                  char *optstr;
-                  format_option (&optstr, option, TRUE, pool);
-                  fprintf (stream, "  %s\n", optstr);
-                }
-            }
-        }
-
-      if (have_options)
-        fprintf (stream, "\n");
-    }
-}
-
-
-
-/* Print a generic (non-command-specific) usage message. */
-void
-svn_cl__print_generic_help (apr_pool_t *pool, FILE *stream)
-{
-  static const char usage[] =
-    "usage: svn <subcommand> [options] [args]\n"
-    "Type \"svn help <subcommand>\" for help on a specific subcommand.\n"
-    "\n"
-    "Most subcommands take file and/or directory arguments, recursing\n"
-    "on the directories.  If no arguments are supplied to such a\n"
-    "command, it will recurse on the current directory (inclusive) by\n" 
-    "default.\n"
-    "\n"
-    "Available subcommands:\n";
-
-  static const char info[] =
-    "Subversion is a tool for revision control.\n"
-    "For additional information, see http://subversion.tigris.org\n";
-
-  int i = 0;
-
-  fprintf (stream, "%s", usage);
-  while (svn_cl__cmd_table[i].name) 
-    {
-      fprintf (stream, "   ");
-      print_command_info (svn_cl__cmd_table + i, FALSE, pool, stream);
-      fprintf (stream, "\n");
-      i++;
-    }
-
-  fprintf (stream, "\n");
-  fprintf (stream, "%s\n", info);
-
-}
-
-
-/* Helper function that will print the usage test of a subcommand
- * given the subcommand name as a char*. This function is also
- * used by subcommands that need to print a usage message */
-
-void
-svn_cl__subcommand_help (const char* subcommand,
-                         apr_pool_t *pool)
-{
-  const svn_cl__cmd_desc_t *cmd =
-    svn_cl__get_canonical_command (subcommand);
-    
-  if (cmd)
-    print_command_info (cmd, TRUE, pool, stdout);
-  else
-    fprintf (stderr, "\"%s\": unknown command.\n\n", subcommand);
-}
-
-
 
 /*** Parsing "X:Y"-style arguments. ***/
 
@@ -771,18 +511,13 @@
 main (int argc, const char * const *argv)
 {
   int ret;
-  apr_status_t apr_err;
   svn_error_t *err;
   apr_pool_t *pool;
-  int opt_id;
-  const char *opt_arg;
-  apr_getopt_t *os;  
+  svn_getopt_t *os;  
   svn_cl__opt_state_t opt_state;
-  int received_opts[SVN_CL__MAX_OPTS];
-  int i, num_opts = 0;
-  const svn_cl__cmd_desc_t *subcommand = NULL;
   svn_boolean_t log_under_version_control = FALSE;
   svn_boolean_t log_is_pathname = FALSE;
+  int i;
 
   /* FIXME: This is a first step towards support for localization in
      `svn'.  In real life, this call would be
@@ -797,11 +532,10 @@
      the default locale at program startup.) */
   setlocale (LC_ALL, "C");
 
-
   apr_initialize ();
   pool = svn_pool_create (NULL);
-  memset (&opt_state, 0, sizeof (opt_state));
 
+  memset (&opt_state, 0, sizeof (opt_state));
   opt_state.start_revision.kind = svn_client_revision_unspecified;
   opt_state.end_revision.kind = svn_client_revision_unspecified;
   
@@ -814,24 +548,18 @@
     }
 
   /* Else, parse options. */
-  apr_getopt_init (&os, pool, argc, argv);
-  os->interleave = 1;
-  while (1)
-    {
-      /* Parse the next option. */
-      apr_err = apr_getopt_long (os, svn_cl__options, &opt_id, &opt_arg);
-      if (APR_STATUS_IS_EOF (apr_err))
-        break;
-      else if (! APR_STATUS_IS_SUCCESS (apr_err))
-        {
-          svn_cl__help (NULL, NULL, pool);
-          svn_pool_destroy (pool);
-          return EXIT_FAILURE;
-        }
+  if (APR_SUCCESS != svn_getopt_init (&os, pool, argc, argv, svn_cl__options, svn_cl__cmd_table))
+    {
+      svn_cl__help (NULL, NULL, pool);
+      svn_pool_destroy (pool);
+      return EXIT_FAILURE;
+    }
 
-      /* Stash the option code in an array before parsing it. */
-      received_opts[num_opts] = opt_id;
-      num_opts++;
+  /* Handle specific command line option issues. */
+  for (i=0; i<os->num_received_opts; ++i)
+    {
+      const int opt_id = os->received_opts[i];
+      const char *opt_arg = os->received_args[i];
 
       switch (opt_id) {
       case 'm':
@@ -998,53 +726,22 @@
      just typos/mistakes.  Whatever the case, the subcommand to
      actually run is svn_cl__help(). */
   if (opt_state.help)
-    subcommand = svn_cl__get_canonical_command ("help");
+    os->subcommand = svn_cl__get_canonical_command ("help", svn_cl__cmd_table);
 
   /* If we're not running the `help' subcommand, then look for a
      subcommand in the first argument. */
-  if (subcommand == NULL)
+  if (os->subcommand == NULL)
     {
-      if (os->ind >= os->argc)
+      if (os->options->ind >= os->options->argc)
         {
           fprintf (stderr, "subcommand argument required\n");
           svn_cl__help (NULL, NULL, pool);
           svn_pool_destroy (pool);
           return EXIT_FAILURE;
         }
-      else
-        {
-          const char *first_arg = os->argv[os->ind++];
-          subcommand = svn_cl__get_canonical_command (first_arg);
-          if (subcommand == NULL)
-            {
-              /* FIXME: should we print "unknown foo" ?? seems ok */
-              fprintf (stderr, "unknown command: %s\n", first_arg);
-              svn_cl__help (NULL, NULL, pool);
-              svn_pool_destroy (pool);
-              return EXIT_FAILURE;
-            }
-        }
     }
 
-  /* If we made it this far, then we definitely have the subcommand,
-     so call it.  But first check that it wasn't passed any
-     inappropriate options. */
-  for (i = 0; i < num_opts; i++)
-    if (! subcommand_takes_option (subcommand, received_opts[i]))
-      {
-        char *optstr;
-        const apr_getopt_option_t *badopt = 
-          svn_cl__get_option_from_enum (received_opts[i], svn_cl__options);
-        format_option (&optstr, badopt, FALSE, pool);
-        fprintf (stderr,
-                 "\nError: subcommand '%s' doesn't accept option '%s'\n\n",
-                 subcommand->name, optstr);
-        svn_cl__subcommand_help (subcommand->name, pool);
-        svn_pool_destroy(pool);
-        return EXIT_FAILURE;
-      }
-
-  if (subcommand->cmd_func == svn_cl__commit)
+  if (os->subcommand->cmd_func == svn_cl__commit)
     {
       /* If the log message file is under revision control, that's
          probably not what the user intended. */
@@ -1076,7 +773,7 @@
         }
     }
 
-  err = (*subcommand->cmd_func) (os, &opt_state, pool);
+  err = (*os->subcommand->cmd_func) (os, &opt_state, pool);
   if (err)
     {
       if (err->apr_err != SVN_ERR_CL_ARG_PARSING_ERROR)
Index: ./subversion/clients/cmdline/propedit-cmd.c
===================================================================
--- ./subversion/clients/cmdline/propedit-cmd.c
+++ ./subversion/clients/cmdline/propedit-cmd.c	Thu Mar 14 16:22:22 2002
@@ -34,22 +34,24 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propedit (apr_getopt_t *os,
-                  svn_cl__opt_state_t *opt_state,
+svn_cl__propedit (svn_getopt_t *os,
+                  void *opt_state_,
                   apr_pool_t *pool)
 {
+  svn_cl__opt_state_t *opt_state = (svn_cl__opt_state_t *)opt_state_;
+  apr_array_header_t *args;
   svn_stringbuf_t *propname;
   apr_array_header_t *targets;
   int i;
 
   /* Validate the input. */
-  SVN_ERR (svn_cl__parse_num_args (os, opt_state, "propedit", 1, pool));
+  SVN_ERR (svn_cl__parse_num_args (&args, os, "propedit", 1, pool));
 
   /* Get the property's name. */
-  propname = ((svn_stringbuf_t **) (opt_state->args->elts))[0];
+  propname = ((svn_stringbuf_t **) (args->elts))[0];
 
   /* Suck up all the remaining arguments into a targets array */
-  targets = svn_cl__args_to_target_array (os, opt_state, pool);
+  targets = svn_cl__args_to_target_array (os, opt_state->targets, pool);
 
   /* Add "." if user passed 0 file arguments */
   svn_cl__push_implicit_dot_target (targets, pool);
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Thu Jul 25 21:32:45 2002