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

PATCH: process arguments in subcommands instead of main()

From: Mo DeJong <mdejong_at_cygnus.com>
Date: 2001-03-17 05:04:31 CET

Here is a patch to implement processing of arguments in
the subcommand instead of doing it all in main. The
current approach of processing all the subcommand
arguments in main() works, but it is a bit hard
to get your brain around and I an not sure it will
be flexible enough to deal with special cases or
new subcommands that may be added in the future.
Much of this was covered in earlier discussion on
the list so I will not try to rehash it here.

High level changes:

1. Removed num_args field from svn_cl__cmd_desc_t struct.

2. Changed svn_cl__cmd_proc_t signature to:

(apr_getopt_t *, svn_cl__opt_state_t *, apr_pool_t *)

3. Processing or arguments is done in the subcommand.

4. Change svn_cl__help so that it will print
   the main program usage if NULL is passed in the
   first argument.

5. Subcommand help text is printed if the user passes
   an incorrect number of arguments.

6. Add a new error type SVN_ERR_ALREADY_PRINTED. When
   svn_handle_error() is called on this type of error
   it will print nothing and return.

7. Add "di" shortcut for diff subcommand to match
   the CVS implementation.

Detailed changes:

2001-03-16 Mo DeJong <mdejong@redhat.com>

        * subversion/client/TODO: Fix number
        of arguments listing for the delete,
        propget, and propset commands. Fix
        minor format issues, add newline at
        EOF to avoid diff warning.
        * subversion/client/add-cmd.c (svn_cl__add):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract target array in the subcommand.
        Print subcommand usage if no targets are passed
        in on the command line.
        * subversion/client/checkout-cmd.c (svn_cl__checkout):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract args array in the subcommand.
        Print subcommand usage if no URLs are passed
        in on the command line.
        * subversion/client/cl.h (svn_cl__cmd_proc_t,
        svn_cl__cmd_desc_t, svn_cl__push_svn_string,
        svn_cl__args_to_target_array, svn_cl__parse_num_args,
        svn_cl__parse_all_args, svn_cl__subcommand_help):
        Change svn_cl__cmd_proc_t signature so that
        it accpets a apr_getopt_t* in the first argument
        and a svn_cl__opt_state_t* in the second argument.
        Remove num_args field from svn_cl__cmd_desc_t.
        Add function prototypes for methods defined in
        main.c.
        * subversion/client/commit-cmd.c (svn_cl__commit):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract target array in the subcommand.
        * subversion/client/delete-cmd.c (svn_cl__delete):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract target array in the subcommand.
        Print subcommand usage if no targets are passed
        in on the command line.
        * subversion/client/diff-cmd.c (svn_cl__diff):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract target array in the subcommand.
        * subversion/client/help-cmd.c (svn_cl__help,
        svn_cl__subcommand_help):
        Account for change in svn_cl__cmd_proc_t signature.
        Print main help text if the first argument to
        svn_cl__help is NULL.
        Extract target array in the subcommand.
        Add new +svn_cl__subcommand_help helper method,
        use it in svn_cl__help.
        * subversion/client/main.c (svn_cl__cmd_table,
        svn_cl__push_svn_string, svn_cl__parse_num_args,
        svn_cl__parse_all_args, svn_cl__args_to_target_array):
        Remove svn_cl__cmd_desc_t num_args initializer.
        Add "di" shortcut for diff to match CVS shortcut.
        Add argument processing helper methods, remove
        arguments and targets processing from main().
        Remove targets array from main(), it is no
        longer passed to a subcommand function.
        Call svn_cl__help with NULL in first two arguments,
        to print main help text. Fix exit status so that
        when an error is encountered, svn will not terminate
        with an exit status of 0.
        * subversion/client/propget-cmd.c (svn_cl__propget):
        Account for change in svn_cl__cmd_proc_t signature.
        Use propname and propval variables to match usage.
        Extract target array in the subcommand.
        * subversion/client/proplist-cmd.c (svn_cl__proplist):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract target array in the subcommand.
        * subversion/client/propset-cmd.c (svn_cl__propset):
        Account for change in svn_cl__cmd_proc_t signature.
        Use propname and propval variables to match usage.
        Extract args and targets arrays in the subcommand.
        Add implicit . if not targets are passed in.
        * subversion/client/status-cmd.c (svn_cl__status):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract target array in the subcommand.
        * subversion/client/update-cmd.c (svn_cl__update):
        Account for change in svn_cl__cmd_proc_t signature.
        Extract target array in the subcommand.

And the patch:

Index: TODO
===================================================================
RCS file: /cvs/subversion/subversion/client/TODO,v
retrieving revision 1.1
diff -u -r1.1 TODO
--- TODO 2001/03/14 07:05:24 1.1
+++ TODO 2001/03/17 03:05:27
@@ -10,15 +10,15 @@
 COMMAND # of non-file args # of file args
 annotate 0 0, 1, or more (implicit dot)
 commit 0 0, 1, or more (implicit dot)
-delete 0 0, 1, or more (implicit dot)
+delete 0 1, or more
 status 0 0, 1, or more (implicit dot)
 update 0 0, 1, or more (implicit dot)
 log 0 0, 1, or more (implicit dot)
 diff ! 0, 1, or more (implicit dot)
-propget 1 0, 1, or more (implicit dot?)
-propset 2 0, 1, or more (implicit dot?)
-add 0 1 or more
-checkout 1 or more 0
+propget 1 0, 1, or more (implicit dot)
+propset 2 0, 1, or more (implicit dot)
+add 0 1 or more
+checkout 1 or more 0
 import 3 0
 
 So the mechanism that we currently have in place will handle
@@ -27,4 +27,5 @@
 different external diff programs.
 
 
-
\ No newline at end of file
+
+
Index: add-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/add-cmd.c,v
retrieving revision 1.11
diff -u -r1.11 add-cmd.c
--- add-cmd.c 2000/11/22 23:48:37 1.11
+++ add-cmd.c 2001/03/17 03:05:28
@@ -31,13 +31,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__add (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__add (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
              apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
+ targets = svn_cl__args_to_target_array (os, pool);
+
   if (targets->nelts)
     for (i = 0; i < targets->nelts; i++)
       {
@@ -48,10 +51,8 @@
       }
   else
     {
- fprintf (stderr, "svn add: arguments required\n");
- err = svn_cl__help (opt_state, targets, pool);
- if (err)
- return err;
+ svn_cl__subcommand_help ("add", pool);
+ return svn_error_create (SVN_ERR_ALREADY_PRINTED, 0, 0, pool, "");
     }
 
   return SVN_NO_ERROR;
Index: checkout-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/checkout-cmd.c,v
retrieving revision 1.16
diff -u -r1.16 checkout-cmd.c
--- checkout-cmd.c 2001/03/16 04:02:50 1.16
+++ checkout-cmd.c 2001/03/17 03:05:28
@@ -30,14 +30,19 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__checkout (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__checkout (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                   apr_pool_t *pool)
 {
   const svn_delta_edit_fns_t *trace_editor;
   void *trace_edit_baton;
   svn_error_t *err;
   int i;
+
+ err = svn_cl__parse_all_args (os, opt_state, "checkout", pool);
+
+ if (err)
+ return err;
 
   /* TODO Fixme: This only works for one repo checkout at a shot. In
      CVS, when we checkout one project and give it a destination
Index: cl.h
===================================================================
RCS file: /cvs/subversion/subversion/client/cl.h,v
retrieving revision 1.39
diff -u -r1.39 cl.h
--- cl.h 2001/03/15 08:39:05 1.39
+++ cl.h 2001/03/17 03:05:28
@@ -21,6 +21,7 @@
 
 /*** Includes. ***/
 #include <apr_tables.h>
+#include <apr_getopt.h>
 
 #include "svn_wc.h"
 #include "svn_string.h"
@@ -62,8 +63,8 @@
  * (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) (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+typedef svn_error_t *(svn_cl__cmd_proc_t) (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                                            apr_pool_t *pool);
 
 
@@ -82,11 +83,6 @@
   /* The function this command invokes. NULL if alias. */
   svn_cl__cmd_proc_t *cmd_func;
 
- /* The number of non-filename arguments the command takes. (e.g. 2
- * for propset, 1 for propget, 0 for most other commands). -1 means
- * "just give me all of the arguments" */
- int num_args;
-
   /* A brief string describing this command, for usage messages. */
   const char *help;
 
@@ -107,7 +103,33 @@
   svn_cl__diff,
   svn_cl__update;
 
-void svn_cl__push_implicit_dot_target(apr_array_header_t *targets,
apr_pool_t *pool);
+void svn_cl__push_svn_string (apr_array_header_t *array,
+ const char *str,
+ apr_pool_t *pool);
+
+apr_array_header_t*
+svn_cl__args_to_target_array (apr_getopt_t *os,
+ apr_pool_t *pool);
+
+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. ***/
Index: commit-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/commit-cmd.c,v
retrieving revision 1.16
diff -u -r1.16 commit-cmd.c
--- commit-cmd.c 2001/03/14 07:42:35 1.16
+++ commit-cmd.c 2001/03/17 03:05:28
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__commit (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__commit (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: delete-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/delete-cmd.c,v
retrieving revision 1.12
diff -u -r1.12 delete-cmd.c
--- delete-cmd.c 2000/11/22 23:48:37 1.12
+++ delete-cmd.c 2001/03/17 03:05:28
@@ -31,13 +31,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__delete (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__delete (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
+ targets = svn_cl__args_to_target_array (os, pool);
+
   if (targets->nelts)
     for (i = 0; i < targets->nelts; i++)
       {
@@ -48,10 +51,8 @@
       }
   else
     {
- fprintf (stderr, "svn delete: arguments required\n");
- err = svn_cl__help (opt_state, targets, pool);
- if (err)
- return err;
+ svn_cl__subcommand_help ("delete", pool);
+ return svn_error_create (SVN_ERR_ALREADY_PRINTED, 0, 0, pool, "");
     }
 
   return SVN_NO_ERROR;
Index: diff-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/diff-cmd.c,v
retrieving revision 1.4
diff -u -r1.4 diff-cmd.c
--- diff-cmd.c 2001/03/13 01:45:34 1.4
+++ diff-cmd.c 2001/03/17 03:05:28
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__diff (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__diff (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
               apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: help-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/help-cmd.c,v
retrieving revision 1.1
diff -u -r1.1 help-cmd.c
--- help-cmd.c 2001/03/02 23:36:16 1.1
+++ help-cmd.c 2001/03/17 03:05:28
@@ -95,29 +95,29 @@
 
 
 /* Print either generic help, or command-specific help for each
- * command in ARGV. OPT_STATE is unused and may be null.
+ * command in os->args. OPT_STATE is unused and may be null.
+ * If OS is null then generic help will always be printed.
  *
  * Unlike all the other command routines, ``help'' has its own
  * option processing. Of course, it does not accept any options :-),
  * just command line args.
  */
 svn_error_t *
-svn_cl__help (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__help (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
               apr_pool_t *pool)
 {
+ apr_array_header_t *targets;
   int i;
 
- if (targets->nelts)
+ if (os)
+ targets = svn_cl__args_to_target_array (os, pool);
+
+ if (os && targets->nelts)
     for (i = 0; i < targets->nelts; i++)
       {
         svn_string_t *this = (((svn_string_t **) (targets)->elts))[i];
- const svn_cl__cmd_desc_t *cmd
- = svn_cl__get_canonical_command (this->data);
- if (cmd)
- print_command_info (cmd, TRUE, pool);
- else
- fprintf (stderr, "\"%s\": unknown command.\n\n", this->data);
+ svn_cl__subcommand_help (this->data, pool);
       }
   else
     print_generic_help (pool);
@@ -125,6 +125,22 @@
   return SVN_NO_ERROR;
 }
 
+/* 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);
+ else
+ fprintf (stderr, "\"%s\": unknown command.\n\n", subcommand);
+}
 
 
 /*
Index: main.c
===================================================================
RCS file: /cvs/subversion/subversion/client/main.c,v
retrieving revision 1.71
diff -u -r1.71 main.c
--- main.c 2001/03/16 21:40:33 1.71
+++ main.c 2001/03/17 03:05:28
@@ -22,7 +22,6 @@
 #include <assert.h>
 
 #include <apr_strings.h>
-#include <apr_getopt.h>
 #include <apr_tables.h>
 #include <apr_general.h>
 
@@ -52,90 +51,171 @@
  */
 const svn_cl__cmd_desc_t svn_cl__cmd_table[] =
 {
- { "add", FALSE, svn_cl__add, 0,
+ { "add", FALSE, svn_cl__add,
     "Add new files and directories to version control.\n"
     "usage: add [TARGETS]\n" },
- { "ad", TRUE, NULL, 0, NULL },
- { "new", TRUE, NULL, 0, NULL },
+ { "ad", TRUE, NULL, NULL },
+ { "new", TRUE, NULL, NULL },
 
- { "checkout", FALSE, svn_cl__checkout, -1,
+ { "checkout", FALSE, svn_cl__checkout,
     "Check out a working directory from a repository.\n"
     "usage: checkout REPOSPATH1 [REPOSPATH2 REPOSPATH3...]\n" },
- { "co", TRUE, NULL, 0, NULL },
+ { "co", TRUE, NULL, NULL },
 
- { "commit", FALSE, svn_cl__commit, 0,
+ { "commit", FALSE, svn_cl__commit,
     "Commit changes from your working copy to the repository.\n"
     "usage: commit [TARGETS]\n" },
- { "ci", TRUE, NULL, 0, NULL },
+ { "ci", TRUE, NULL, NULL },
 
- { "delete", FALSE, svn_cl__delete, 0,
+ { "delete", FALSE, svn_cl__delete,
     "Remove files and directories from version control.\n"
     "usage: delete [TARGETS]\n" },
- { "del", TRUE, NULL, 0, NULL },
- { "remove", TRUE, NULL, 0, NULL },
- { "rm", TRUE, NULL, 0, NULL },
+ { "del", TRUE, NULL, NULL },
+ { "remove", TRUE, NULL, NULL },
+ { "rm", TRUE, NULL, NULL },
 
- { "help", FALSE, svn_cl__help, 0,
+ { "help", FALSE, svn_cl__help,
     "Display this usage message.\n"
     "usage: help [SUBCOMMAND1 [SUBCOMMAND2] ...]\n" },
- { "?", TRUE, NULL, 0, NULL },
- { "h", TRUE, NULL, 0, NULL },
+ { "?", TRUE, NULL, NULL },
+ { "h", TRUE, NULL, NULL },
   /* 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
      support them explicitly. */
 
- { "proplist", FALSE, svn_cl__proplist, 0,
+ { "proplist", FALSE, svn_cl__proplist,
     "List all properties for given files and directories.\n"
     "usage: proplist [TARGETS]\n" },
- { "plist", TRUE, NULL, 0, NULL },
- { "pl", TRUE, NULL, 0, NULL },
+ { "plist", TRUE, NULL, NULL },
+ { "pl", TRUE, NULL, NULL },
 
- { "propget", FALSE, svn_cl__propget, 1,
+ { "propget", FALSE, svn_cl__propget,
     "Get the value of property PROPNAME on files and directories.\n"
     "usage: propget PROPNAME [TARGETS]\n" },
- { "pget", TRUE, NULL, 1, NULL },
- { "pg", TRUE, NULL, 1, NULL },
+ { "pget", TRUE, NULL, NULL },
+ { "pg", TRUE, NULL, NULL },
 
- { "propset", FALSE, svn_cl__propset, 2,
+ { "propset", FALSE, svn_cl__propset,
     "Set property PROPNAME to PROPVAL on files and directories.\n"
     "usage: propset PROPNAME [PROPVAL | --valfile VALFILE] "
     "[TARGETS]\n"},
- { "pset", TRUE, NULL, 2, NULL },
- { "ps", TRUE, NULL, 2, NULL },
+ { "pset", TRUE, NULL, NULL },
+ { "ps", TRUE, NULL, NULL },
 
- { "status", FALSE, svn_cl__status, 0,
+ { "status", FALSE, svn_cl__status,
     "Print the status of working copy files and directories.\n"
     "usage: status [TARGETS]\n" },
- { "stat", TRUE, NULL, 0, NULL },
- { "st", TRUE, NULL, 0, NULL },
+ { "stat", TRUE, NULL, NULL },
+ { "st", TRUE, NULL, NULL },
 
- { "diff", FALSE, svn_cl__diff, 0,
+ { "diff", FALSE, svn_cl__diff,
     "Display local file changes as contextual diffs.\n"
     "usage: diff [TARGETS]\n" },
- { "df", TRUE, NULL, 0, NULL },
+ { "df", TRUE, NULL, NULL },
+ { "di", TRUE, NULL, NULL },
 
- { "update", FALSE, svn_cl__update, 0,
+ { "update", FALSE, svn_cl__update,
     "Bring changes from the repository into the working copy.\n"
     "usage: update [TARGETS]\n" },
- { "up", TRUE, NULL, 0, NULL },
- { NULL, FALSE, NULL, 0, NULL }
+ { "up", TRUE, NULL, NULL },
+ { NULL, FALSE, NULL, NULL }
 };
 
+/* Create a SVN string from the char* and add it to the array */
+void svn_cl__push_svn_string (apr_array_header_t *array,
+ const char *str,
+ apr_pool_t *pool)
+{
+ (*((svn_string_t **) apr_array_push (array)))
+ = svn_string_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 */
 void
-svn_cl__push_implicit_dot_target(apr_array_header_t *targets, apr_pool_t *pool)
+svn_cl__push_implicit_dot_target (apr_array_header_t *targets, apr_pool_t *pool)
+{
+ if (targets->nelts == 0)
+ svn_cl__push_svn_string (targets, ".", pool);
+ 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, 0, sizeof (svn_string_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_ALREADY_PRINTED, 0, 0, pool, "");
+ }
+ svn_cl__push_svn_string (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)
 {
- if (targets->nelts == 0) {
- (*((svn_string_t **) apr_array_push (targets)))
- = svn_string_create (".", pool);
- }
- assert(targets->nelts);
+ opt_state->args = apr_array_make (pool, 0, sizeof (svn_string_t *));
+
+ if (os->ind >= os->argc)
+ {
+ svn_cl__subcommand_help (subcommand, pool);
+ return svn_error_create (SVN_ERR_ALREADY_PRINTED, 0, 0, pool, "");
+ }
+
+ while (os->ind < os->argc)
+ {
+ svn_cl__push_svn_string (opt_state->args, os->argv[os->ind++], pool);
+ }
+
+ return SVN_NO_ERROR;
 }
 
+/* Create a targets array and add all the remaining arguments
+ * to it. */
+apr_array_header_t*
+svn_cl__args_to_target_array (apr_getopt_t *os,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *targets =
+ apr_array_make (pool, 0, sizeof (svn_string_t *));
+
+ for (; os->ind < os->argc; os->ind++)
+ {
+ svn_cl__push_svn_string (targets, os->argv[os->ind], pool);
+ }
+
+ /* kff todo: need to remove redundancies from targets before
+ passing it to the cmd_func. */
+
+ return targets;
+}
+
+
 /* Return the entry in svn_cl__cmd_table whose name matches CMD_NAME,
  * or null if none. CMD_NAME may be an alias, in which case the alias
  * entry will be returned (so caller may need to canonicalize result). */
@@ -190,7 +270,6 @@
   apr_getopt_t *os;
   svn_cl__opt_state_t opt_state;
   const svn_cl__cmd_desc_t *subcommand = NULL;
- apr_array_header_t *targets; /* file/dir args from the command line */
 
   static const apr_getopt_option_t options[] =
   {
@@ -209,12 +288,10 @@
   memset (&opt_state, 0, sizeof (opt_state));
   opt_state.revision = SVN_INVALID_REVNUM;
 
- targets = apr_array_make (pool, 0, sizeof (svn_string_t *));
-
   /* No args? Show usage. */
   if (argc <= 1)
     {
- svn_cl__help (NULL, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
       apr_pool_destroy (pool);
       return EXIT_FAILURE;
     }
@@ -230,7 +307,7 @@
         break;
       else if (! APR_STATUS_IS_SUCCESS (apr_err))
         {
- svn_cl__help (NULL, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
           apr_pool_destroy (pool);
           return EXIT_FAILURE;
         }
@@ -292,7 +369,7 @@
       if (os->ind >= os->argc)
         {
           fprintf (stderr, "subcommand argument required\n");
- svn_cl__help (NULL, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
           apr_pool_destroy (pool);
           return EXIT_FAILURE;
         }
@@ -302,76 +379,29 @@
           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, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
               apr_pool_destroy (pool);
               return EXIT_FAILURE;
             }
         }
     }
   
- /* If we made it this far, then we definitely have the subcommand. */
+ /* If we made it this far, then we definitely have the subcommand, so call it. */
 
- /* Below, we parse out some of the regular arguments, because
- * certain subcommands consume them. For example, both propget and
- * propset need NAME, and propset needs VALUE in addition. */
- opt_state.args = apr_array_make (pool, 0, sizeof (svn_string_t *));
-
- if (subcommand->num_args > 0) {
- const char *this_arg;
- int i;
- /* loop for num_args and add each arg to the args array */
- for (i = 0; i < subcommand->num_args; i++) {
- if (os->ind >= os->argc) {
- const char *plural = "s";
- fprintf (stderr, "ERROR: The %s command requires %i argument%s\n",
- subcommand->name, subcommand->num_args,
- (subcommand->num_args == 1) ? "" : plural);
- fprintf (stderr, "Help for %s:\n%s", subcommand->name, subcommand->help);
- apr_pool_destroy (pool);
- return EXIT_FAILURE;
- }
- this_arg = os->argv[os->ind++];
- (*((svn_string_t **) apr_array_push (opt_state.args)))
- = svn_string_create (this_arg, pool);
-
- }
- }
- /* greedily suck up all args if num_args is a negative number. */
- else if (subcommand->num_args == -1) {
- if (os->ind >= os->argc) {
- fprintf (stderr, "ERROR: The %s command requires at least one argument\n",
- subcommand->name);
- fprintf (stderr, "Help for %s:\n%s", subcommand->name, subcommand->help);
+ err = (*subcommand->cmd_func) (os, &opt_state, pool);
+ if (err)
+ {
+ svn_handle_error (err, stdout, 0);
       apr_pool_destroy (pool);
       return EXIT_FAILURE;
- }
-
- while (os->ind < os->argc) {
- const char *this_arg = os->argv[os->ind++];
- (*((svn_string_t **) apr_array_push (opt_state.args)))
- = svn_string_create (this_arg, pool);
     }
- }
-
- /* Do the regular arguments (target files and target dirs). */
- for (; os->ind < os->argc; os->ind++)
+ else
     {
- const char *this_arg = os->argv[os->ind];
- (*((svn_string_t **) apr_array_push (targets)))
- = svn_string_create (this_arg, pool);
+ apr_pool_destroy (pool);
+ return EXIT_SUCCESS;
     }
-
- /* kff todo: need to remove redundancies from targets before
- passing it to the cmd_func. */
-
- /* Run the subcommand. */
- err = (*subcommand->cmd_func) (&opt_state, targets, pool);
- if (err)
- svn_handle_error (err, stdout, 0);
-
- apr_pool_destroy (pool);
- return EXIT_SUCCESS;
 }
 
 
Index: propget-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/propget-cmd.c,v
retrieving revision 1.11
diff -u -r1.11 propget-cmd.c
--- propget-cmd.c 2001/03/13 01:45:34 1.11
+++ propget-cmd.c 2001/03/17 03:05:28
@@ -30,30 +30,43 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propget (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__propget (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                  apr_pool_t *pool)
 {
- svn_string_t *name = ((svn_string_t **) (opt_state->args->elts))[0];
+ svn_string_t *propname;
   apr_hash_t *prop_hash = apr_hash_make (pool);
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
+ /* PROPNAME is first argument */
+ err = svn_cl__parse_num_args (os, opt_state,
+ "propget", 1, pool);
+
+ if (err)
+ return err;
+
+ propname = ((svn_string_t **) (opt_state->args->elts))[0];
+
+ /* suck up all the remaining arguments into a targets array */
+ targets = svn_cl__args_to_target_array (os, pool);
+
   /* Add "." if user passed 0 file arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
 
   for (i = 0; i < targets->nelts; i++)
     {
- svn_string_t *value;
+ svn_string_t *propval;
       svn_string_t *target = ((svn_string_t **) (targets->elts))[i];
- err = svn_wc_prop_get (&value, name, target, pool);
+ err = svn_wc_prop_get (&propval, propname, target, pool);
       if (err)
         return err;
 
       /* kff todo: this seems like an odd way to do this... */
 
- apr_hash_set (prop_hash, name->data, name->len,
- value);
+ apr_hash_set (prop_hash, propname->data, propname->len,
+ propval);
       svn_cl__print_prop_hash (prop_hash, pool);
     }
 
Index: proplist-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/proplist-cmd.c,v
retrieving revision 1.12
diff -u -r1.12 proplist-cmd.c
--- proplist-cmd.c 2001/03/13 01:45:34 1.12
+++ proplist-cmd.c 2001/03/17 03:05:28
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__proplist (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__proplist (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                   apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: propset-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/propset-cmd.c,v
retrieving revision 1.9
diff -u -r1.9 propset-cmd.c
--- propset-cmd.c 2001/03/13 01:44:41 1.9
+++ propset-cmd.c 2001/03/17 03:05:28
@@ -30,41 +30,52 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propset (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__propset (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                  apr_pool_t *pool)
 {
- svn_string_t *name = ((svn_string_t **) (opt_state->args->elts))[0];
- svn_string_t *value = ((svn_string_t **) (opt_state->args->elts))[1];
+ svn_string_t *propname;
+ svn_string_t *propval;
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
- if (! strcmp (value->data, ""))
- /* The user wants to delete the property. */
- value = NULL;
-
- if (targets->nelts)
- for (i = 0; i < targets->nelts; i++)
- {
- svn_string_t *target = ((svn_string_t **) (targets->elts))[i];
- err = svn_wc_prop_set (name, value, target, pool);
- if (err)
- return err;
-
- /* fitz todo: make these print out only when VERBOSE */
- if (value)
- printf ("property `%s' set on %s.\n",
- name->data, target->data);
- else
- printf ("property `%s' deleted from %s\n",
- name->data, target->data);
- }
- else
+ /* PROPNAME and PROPVAL expected as first 2 arguments */
+ err = svn_cl__parse_num_args (os, opt_state,
+ "propset", 2, pool);
+
+ if (err)
+ return err;
+
+ propname = ((svn_string_t **) (opt_state->args->elts))[0];
+ propval = ((svn_string_t **) (opt_state->args->elts))[1];
+
+ if (! strcmp (propval->data, ""))
+ {
+ /* The user wants to delete the property. */
+ propval = NULL;
+ }
+
+ /* suck up all the remaining arguments into a targets array */
+ targets = svn_cl__args_to_target_array (os, pool);
+
+ /* Add "." if user passed 0 file arguments */
+ svn_cl__push_implicit_dot_target(targets, pool);
+
+ for (i = 0; i < targets->nelts; i++)
     {
- fprintf (stderr, "svn propset: arguments required\n");
- err = svn_cl__help (opt_state, targets, pool);
+ svn_string_t *target = ((svn_string_t **) (targets->elts))[i];
+ err = svn_wc_prop_set (propname, propval, target, pool);
       if (err)
         return err;
+
+ /* fitz todo: make these print out only when VERBOSE */
+ if (propval)
+ printf ("property `%s' set on %s.\n",
+ propname->data, target->data);
+ else
+ printf ("property `%s' deleted from %s\n",
+ propname->data, target->data);
     }
 
   return SVN_NO_ERROR;
Index: status-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/status-cmd.c,v
retrieving revision 1.12
diff -u -r1.12 status-cmd.c
--- status-cmd.c 2001/03/13 01:45:34 1.12
+++ status-cmd.c 2001/03/17 03:05:28
@@ -30,13 +30,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__status (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__status (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
   apr_hash_t *statushash;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: update-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/update-cmd.c,v
retrieving revision 1.14
diff -u -r1.14 update-cmd.c
--- update-cmd.c 2001/03/13 01:45:34 1.14
+++ update-cmd.c 2001/03/17 03:05:29
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__update (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__update (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);

P.S.

I also attached the patch file in case it
gets hosed by the mail client.

Mo DeJong
Red Hat Inc

Index: TODO
===================================================================
RCS file: /cvs/subversion/subversion/client/TODO,v
retrieving revision 1.1
diff -u -r1.1 TODO
--- TODO 2001/03/14 07:05:24 1.1
+++ TODO 2001/03/17 03:05:27
@@ -10,15 +10,15 @@
 COMMAND # of non-file args # of file args
 annotate 0 0, 1, or more (implicit dot)
 commit 0 0, 1, or more (implicit dot)
-delete 0 0, 1, or more (implicit dot)
+delete 0 1, or more
 status 0 0, 1, or more (implicit dot)
 update 0 0, 1, or more (implicit dot)
 log 0 0, 1, or more (implicit dot)
 diff ! 0, 1, or more (implicit dot)
-propget 1 0, 1, or more (implicit dot?)
-propset 2 0, 1, or more (implicit dot?)
-add 0 1 or more
-checkout 1 or more 0
+propget 1 0, 1, or more (implicit dot)
+propset 2 0, 1, or more (implicit dot)
+add 0 1 or more
+checkout 1 or more 0
 import 3 0
 
 So the mechanism that we currently have in place will handle
@@ -27,4 +27,5 @@
 different external diff programs.
 
 
-
\ No newline at end of file
+
+
Index: add-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/add-cmd.c,v
retrieving revision 1.11
diff -u -r1.11 add-cmd.c
--- add-cmd.c 2000/11/22 23:48:37 1.11
+++ add-cmd.c 2001/03/17 03:05:28
@@ -31,13 +31,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__add (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__add (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
              apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
+ targets = svn_cl__args_to_target_array (os, pool);
+
   if (targets->nelts)
     for (i = 0; i < targets->nelts; i++)
       {
@@ -48,10 +51,8 @@
       }
   else
     {
- fprintf (stderr, "svn add: arguments required\n");
- err = svn_cl__help (opt_state, targets, pool);
- if (err)
- return err;
+ svn_cl__subcommand_help ("add", pool);
+ return svn_error_create (SVN_ERR_ALREADY_PRINTED, 0, 0, pool, "");
     }
 
   return SVN_NO_ERROR;
Index: checkout-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/checkout-cmd.c,v
retrieving revision 1.16
diff -u -r1.16 checkout-cmd.c
--- checkout-cmd.c 2001/03/16 04:02:50 1.16
+++ checkout-cmd.c 2001/03/17 03:05:28
@@ -30,14 +30,19 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__checkout (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__checkout (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                   apr_pool_t *pool)
 {
   const svn_delta_edit_fns_t *trace_editor;
   void *trace_edit_baton;
   svn_error_t *err;
   int i;
+
+ err = svn_cl__parse_all_args (os, opt_state, "checkout", pool);
+
+ if (err)
+ return err;
 
   /* TODO Fixme: This only works for one repo checkout at a shot. In
      CVS, when we checkout one project and give it a destination
Index: cl.h
===================================================================
RCS file: /cvs/subversion/subversion/client/cl.h,v
retrieving revision 1.39
diff -u -r1.39 cl.h
--- cl.h 2001/03/15 08:39:05 1.39
+++ cl.h 2001/03/17 03:05:28
@@ -21,6 +21,7 @@
 
 /*** Includes. ***/
 #include <apr_tables.h>
+#include <apr_getopt.h>
 
 #include "svn_wc.h"
 #include "svn_string.h"
@@ -62,8 +63,8 @@
  * (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) (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+typedef svn_error_t *(svn_cl__cmd_proc_t) (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                                            apr_pool_t *pool);
 
 
@@ -82,11 +83,6 @@
   /* The function this command invokes. NULL if alias. */
   svn_cl__cmd_proc_t *cmd_func;
 
- /* The number of non-filename arguments the command takes. (e.g. 2
- * for propset, 1 for propget, 0 for most other commands). -1 means
- * "just give me all of the arguments" */
- int num_args;
-
   /* A brief string describing this command, for usage messages. */
   const char *help;
 
@@ -107,7 +103,33 @@
   svn_cl__diff,
   svn_cl__update;
 
-void svn_cl__push_implicit_dot_target(apr_array_header_t *targets, apr_pool_t *pool);
+void svn_cl__push_svn_string (apr_array_header_t *array,
+ const char *str,
+ apr_pool_t *pool);
+
+apr_array_header_t*
+svn_cl__args_to_target_array (apr_getopt_t *os,
+ apr_pool_t *pool);
+
+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. ***/
Index: commit-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/commit-cmd.c,v
retrieving revision 1.16
diff -u -r1.16 commit-cmd.c
--- commit-cmd.c 2001/03/14 07:42:35 1.16
+++ commit-cmd.c 2001/03/17 03:05:28
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__commit (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__commit (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: delete-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/delete-cmd.c,v
retrieving revision 1.12
diff -u -r1.12 delete-cmd.c
--- delete-cmd.c 2000/11/22 23:48:37 1.12
+++ delete-cmd.c 2001/03/17 03:05:28
@@ -31,13 +31,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__delete (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__delete (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
+ targets = svn_cl__args_to_target_array (os, pool);
+
   if (targets->nelts)
     for (i = 0; i < targets->nelts; i++)
       {
@@ -48,10 +51,8 @@
       }
   else
     {
- fprintf (stderr, "svn delete: arguments required\n");
- err = svn_cl__help (opt_state, targets, pool);
- if (err)
- return err;
+ svn_cl__subcommand_help ("delete", pool);
+ return svn_error_create (SVN_ERR_ALREADY_PRINTED, 0, 0, pool, "");
     }
 
   return SVN_NO_ERROR;
Index: diff-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/diff-cmd.c,v
retrieving revision 1.4
diff -u -r1.4 diff-cmd.c
--- diff-cmd.c 2001/03/13 01:45:34 1.4
+++ diff-cmd.c 2001/03/17 03:05:28
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__diff (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__diff (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
               apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: help-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/help-cmd.c,v
retrieving revision 1.1
diff -u -r1.1 help-cmd.c
--- help-cmd.c 2001/03/02 23:36:16 1.1
+++ help-cmd.c 2001/03/17 03:05:28
@@ -95,29 +95,29 @@
 
 
 /* Print either generic help, or command-specific help for each
- * command in ARGV. OPT_STATE is unused and may be null.
+ * command in os->args. OPT_STATE is unused and may be null.
+ * If OS is null then generic help will always be printed.
  *
  * Unlike all the other command routines, ``help'' has its own
  * option processing. Of course, it does not accept any options :-),
  * just command line args.
  */
 svn_error_t *
-svn_cl__help (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__help (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
               apr_pool_t *pool)
 {
+ apr_array_header_t *targets;
   int i;
 
- if (targets->nelts)
+ if (os)
+ targets = svn_cl__args_to_target_array (os, pool);
+
+ if (os && targets->nelts)
     for (i = 0; i < targets->nelts; i++)
       {
         svn_string_t *this = (((svn_string_t **) (targets)->elts))[i];
- const svn_cl__cmd_desc_t *cmd
- = svn_cl__get_canonical_command (this->data);
- if (cmd)
- print_command_info (cmd, TRUE, pool);
- else
- fprintf (stderr, "\"%s\": unknown command.\n\n", this->data);
+ svn_cl__subcommand_help (this->data, pool);
       }
   else
     print_generic_help (pool);
@@ -125,6 +125,22 @@
   return SVN_NO_ERROR;
 }
 
+/* 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);
+ else
+ fprintf (stderr, "\"%s\": unknown command.\n\n", subcommand);
+}
 
 
 /*
Index: main.c
===================================================================
RCS file: /cvs/subversion/subversion/client/main.c,v
retrieving revision 1.71
diff -u -r1.71 main.c
--- main.c 2001/03/16 21:40:33 1.71
+++ main.c 2001/03/17 03:05:28
@@ -22,7 +22,6 @@
 #include <assert.h>
 
 #include <apr_strings.h>
-#include <apr_getopt.h>
 #include <apr_tables.h>
 #include <apr_general.h>
 
@@ -52,90 +51,171 @@
  */
 const svn_cl__cmd_desc_t svn_cl__cmd_table[] =
 {
- { "add", FALSE, svn_cl__add, 0,
+ { "add", FALSE, svn_cl__add,
     "Add new files and directories to version control.\n"
     "usage: add [TARGETS]\n" },
- { "ad", TRUE, NULL, 0, NULL },
- { "new", TRUE, NULL, 0, NULL },
+ { "ad", TRUE, NULL, NULL },
+ { "new", TRUE, NULL, NULL },
 
- { "checkout", FALSE, svn_cl__checkout, -1,
+ { "checkout", FALSE, svn_cl__checkout,
     "Check out a working directory from a repository.\n"
     "usage: checkout REPOSPATH1 [REPOSPATH2 REPOSPATH3...]\n" },
- { "co", TRUE, NULL, 0, NULL },
+ { "co", TRUE, NULL, NULL },
 
- { "commit", FALSE, svn_cl__commit, 0,
+ { "commit", FALSE, svn_cl__commit,
     "Commit changes from your working copy to the repository.\n"
     "usage: commit [TARGETS]\n" },
- { "ci", TRUE, NULL, 0, NULL },
+ { "ci", TRUE, NULL, NULL },
 
- { "delete", FALSE, svn_cl__delete, 0,
+ { "delete", FALSE, svn_cl__delete,
     "Remove files and directories from version control.\n"
     "usage: delete [TARGETS]\n" },
- { "del", TRUE, NULL, 0, NULL },
- { "remove", TRUE, NULL, 0, NULL },
- { "rm", TRUE, NULL, 0, NULL },
+ { "del", TRUE, NULL, NULL },
+ { "remove", TRUE, NULL, NULL },
+ { "rm", TRUE, NULL, NULL },
 
- { "help", FALSE, svn_cl__help, 0,
+ { "help", FALSE, svn_cl__help,
     "Display this usage message.\n"
     "usage: help [SUBCOMMAND1 [SUBCOMMAND2] ...]\n" },
- { "?", TRUE, NULL, 0, NULL },
- { "h", TRUE, NULL, 0, NULL },
+ { "?", TRUE, NULL, NULL },
+ { "h", TRUE, NULL, NULL },
   /* 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
      support them explicitly. */
 
- { "proplist", FALSE, svn_cl__proplist, 0,
+ { "proplist", FALSE, svn_cl__proplist,
     "List all properties for given files and directories.\n"
     "usage: proplist [TARGETS]\n" },
- { "plist", TRUE, NULL, 0, NULL },
- { "pl", TRUE, NULL, 0, NULL },
+ { "plist", TRUE, NULL, NULL },
+ { "pl", TRUE, NULL, NULL },
 
- { "propget", FALSE, svn_cl__propget, 1,
+ { "propget", FALSE, svn_cl__propget,
     "Get the value of property PROPNAME on files and directories.\n"
     "usage: propget PROPNAME [TARGETS]\n" },
- { "pget", TRUE, NULL, 1, NULL },
- { "pg", TRUE, NULL, 1, NULL },
+ { "pget", TRUE, NULL, NULL },
+ { "pg", TRUE, NULL, NULL },
 
- { "propset", FALSE, svn_cl__propset, 2,
+ { "propset", FALSE, svn_cl__propset,
     "Set property PROPNAME to PROPVAL on files and directories.\n"
     "usage: propset PROPNAME [PROPVAL | --valfile VALFILE] "
     "[TARGETS]\n"},
- { "pset", TRUE, NULL, 2, NULL },
- { "ps", TRUE, NULL, 2, NULL },
+ { "pset", TRUE, NULL, NULL },
+ { "ps", TRUE, NULL, NULL },
 
- { "status", FALSE, svn_cl__status, 0,
+ { "status", FALSE, svn_cl__status,
     "Print the status of working copy files and directories.\n"
     "usage: status [TARGETS]\n" },
- { "stat", TRUE, NULL, 0, NULL },
- { "st", TRUE, NULL, 0, NULL },
+ { "stat", TRUE, NULL, NULL },
+ { "st", TRUE, NULL, NULL },
 
- { "diff", FALSE, svn_cl__diff, 0,
+ { "diff", FALSE, svn_cl__diff,
     "Display local file changes as contextual diffs.\n"
     "usage: diff [TARGETS]\n" },
- { "df", TRUE, NULL, 0, NULL },
+ { "df", TRUE, NULL, NULL },
+ { "di", TRUE, NULL, NULL },
 
- { "update", FALSE, svn_cl__update, 0,
+ { "update", FALSE, svn_cl__update,
     "Bring changes from the repository into the working copy.\n"
     "usage: update [TARGETS]\n" },
- { "up", TRUE, NULL, 0, NULL },
- { NULL, FALSE, NULL, 0, NULL }
+ { "up", TRUE, NULL, NULL },
+ { NULL, FALSE, NULL, NULL }
 };
 
+/* Create a SVN string from the char* and add it to the array */
+void svn_cl__push_svn_string (apr_array_header_t *array,
+ const char *str,
+ apr_pool_t *pool)
+{
+ (*((svn_string_t **) apr_array_push (array)))
+ = svn_string_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 */
 void
-svn_cl__push_implicit_dot_target(apr_array_header_t *targets, apr_pool_t *pool)
+svn_cl__push_implicit_dot_target (apr_array_header_t *targets, apr_pool_t *pool)
+{
+ if (targets->nelts == 0)
+ svn_cl__push_svn_string (targets, ".", pool);
+ 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, 0, sizeof (svn_string_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_ALREADY_PRINTED, 0, 0, pool, "");
+ }
+ svn_cl__push_svn_string (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)
 {
- if (targets->nelts == 0) {
- (*((svn_string_t **) apr_array_push (targets)))
- = svn_string_create (".", pool);
- }
- assert(targets->nelts);
+ opt_state->args = apr_array_make (pool, 0, sizeof (svn_string_t *));
+
+ if (os->ind >= os->argc)
+ {
+ svn_cl__subcommand_help (subcommand, pool);
+ return svn_error_create (SVN_ERR_ALREADY_PRINTED, 0, 0, pool, "");
+ }
+
+ while (os->ind < os->argc)
+ {
+ svn_cl__push_svn_string (opt_state->args, os->argv[os->ind++], pool);
+ }
+
+ return SVN_NO_ERROR;
 }
 
+/* Create a targets array and add all the remaining arguments
+ * to it. */
+apr_array_header_t*
+svn_cl__args_to_target_array (apr_getopt_t *os,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *targets =
+ apr_array_make (pool, 0, sizeof (svn_string_t *));
+
+ for (; os->ind < os->argc; os->ind++)
+ {
+ svn_cl__push_svn_string (targets, os->argv[os->ind], pool);
+ }
+
+ /* kff todo: need to remove redundancies from targets before
+ passing it to the cmd_func. */
+
+ return targets;
+}
+
+
 /* Return the entry in svn_cl__cmd_table whose name matches CMD_NAME,
  * or null if none. CMD_NAME may be an alias, in which case the alias
  * entry will be returned (so caller may need to canonicalize result). */
@@ -190,7 +270,6 @@
   apr_getopt_t *os;
   svn_cl__opt_state_t opt_state;
   const svn_cl__cmd_desc_t *subcommand = NULL;
- apr_array_header_t *targets; /* file/dir args from the command line */
 
   static const apr_getopt_option_t options[] =
   {
@@ -209,12 +288,10 @@
   memset (&opt_state, 0, sizeof (opt_state));
   opt_state.revision = SVN_INVALID_REVNUM;
 
- targets = apr_array_make (pool, 0, sizeof (svn_string_t *));
-
   /* No args? Show usage. */
   if (argc <= 1)
     {
- svn_cl__help (NULL, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
       apr_pool_destroy (pool);
       return EXIT_FAILURE;
     }
@@ -230,7 +307,7 @@
         break;
       else if (! APR_STATUS_IS_SUCCESS (apr_err))
         {
- svn_cl__help (NULL, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
           apr_pool_destroy (pool);
           return EXIT_FAILURE;
         }
@@ -292,7 +369,7 @@
       if (os->ind >= os->argc)
         {
           fprintf (stderr, "subcommand argument required\n");
- svn_cl__help (NULL, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
           apr_pool_destroy (pool);
           return EXIT_FAILURE;
         }
@@ -302,76 +379,29 @@
           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, targets, pool);
+ svn_cl__help (NULL, NULL, pool);
               apr_pool_destroy (pool);
               return EXIT_FAILURE;
             }
         }
     }
   
- /* If we made it this far, then we definitely have the subcommand. */
+ /* If we made it this far, then we definitely have the subcommand, so call it. */
 
- /* Below, we parse out some of the regular arguments, because
- * certain subcommands consume them. For example, both propget and
- * propset need NAME, and propset needs VALUE in addition. */
- opt_state.args = apr_array_make (pool, 0, sizeof (svn_string_t *));
-
- if (subcommand->num_args > 0) {
- const char *this_arg;
- int i;
- /* loop for num_args and add each arg to the args array */
- for (i = 0; i < subcommand->num_args; i++) {
- if (os->ind >= os->argc) {
- const char *plural = "s";
- fprintf (stderr, "ERROR: The %s command requires %i argument%s\n",
- subcommand->name, subcommand->num_args,
- (subcommand->num_args == 1) ? "" : plural);
- fprintf (stderr, "Help for %s:\n%s", subcommand->name, subcommand->help);
- apr_pool_destroy (pool);
- return EXIT_FAILURE;
- }
- this_arg = os->argv[os->ind++];
- (*((svn_string_t **) apr_array_push (opt_state.args)))
- = svn_string_create (this_arg, pool);
-
- }
- }
- /* greedily suck up all args if num_args is a negative number. */
- else if (subcommand->num_args == -1) {
- if (os->ind >= os->argc) {
- fprintf (stderr, "ERROR: The %s command requires at least one argument\n",
- subcommand->name);
- fprintf (stderr, "Help for %s:\n%s", subcommand->name, subcommand->help);
+ err = (*subcommand->cmd_func) (os, &opt_state, pool);
+ if (err)
+ {
+ svn_handle_error (err, stdout, 0);
       apr_pool_destroy (pool);
       return EXIT_FAILURE;
- }
-
- while (os->ind < os->argc) {
- const char *this_arg = os->argv[os->ind++];
- (*((svn_string_t **) apr_array_push (opt_state.args)))
- = svn_string_create (this_arg, pool);
     }
- }
-
- /* Do the regular arguments (target files and target dirs). */
- for (; os->ind < os->argc; os->ind++)
+ else
     {
- const char *this_arg = os->argv[os->ind];
- (*((svn_string_t **) apr_array_push (targets)))
- = svn_string_create (this_arg, pool);
+ apr_pool_destroy (pool);
+ return EXIT_SUCCESS;
     }
-
- /* kff todo: need to remove redundancies from targets before
- passing it to the cmd_func. */
-
- /* Run the subcommand. */
- err = (*subcommand->cmd_func) (&opt_state, targets, pool);
- if (err)
- svn_handle_error (err, stdout, 0);
-
- apr_pool_destroy (pool);
- return EXIT_SUCCESS;
 }
 
 
Index: propget-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/propget-cmd.c,v
retrieving revision 1.11
diff -u -r1.11 propget-cmd.c
--- propget-cmd.c 2001/03/13 01:45:34 1.11
+++ propget-cmd.c 2001/03/17 03:05:28
@@ -30,30 +30,43 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propget (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__propget (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                  apr_pool_t *pool)
 {
- svn_string_t *name = ((svn_string_t **) (opt_state->args->elts))[0];
+ svn_string_t *propname;
   apr_hash_t *prop_hash = apr_hash_make (pool);
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
+ /* PROPNAME is first argument */
+ err = svn_cl__parse_num_args (os, opt_state,
+ "propget", 1, pool);
+
+ if (err)
+ return err;
+
+ propname = ((svn_string_t **) (opt_state->args->elts))[0];
+
+ /* suck up all the remaining arguments into a targets array */
+ targets = svn_cl__args_to_target_array (os, pool);
+
   /* Add "." if user passed 0 file arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
 
   for (i = 0; i < targets->nelts; i++)
     {
- svn_string_t *value;
+ svn_string_t *propval;
       svn_string_t *target = ((svn_string_t **) (targets->elts))[i];
- err = svn_wc_prop_get (&value, name, target, pool);
+ err = svn_wc_prop_get (&propval, propname, target, pool);
       if (err)
         return err;
 
       /* kff todo: this seems like an odd way to do this... */
 
- apr_hash_set (prop_hash, name->data, name->len,
- value);
+ apr_hash_set (prop_hash, propname->data, propname->len,
+ propval);
       svn_cl__print_prop_hash (prop_hash, pool);
     }
 
Index: proplist-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/proplist-cmd.c,v
retrieving revision 1.12
diff -u -r1.12 proplist-cmd.c
--- proplist-cmd.c 2001/03/13 01:45:34 1.12
+++ proplist-cmd.c 2001/03/17 03:05:28
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__proplist (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__proplist (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                   apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: propset-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/propset-cmd.c,v
retrieving revision 1.9
diff -u -r1.9 propset-cmd.c
--- propset-cmd.c 2001/03/13 01:44:41 1.9
+++ propset-cmd.c 2001/03/17 03:05:28
@@ -30,41 +30,52 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__propset (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__propset (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                  apr_pool_t *pool)
 {
- svn_string_t *name = ((svn_string_t **) (opt_state->args->elts))[0];
- svn_string_t *value = ((svn_string_t **) (opt_state->args->elts))[1];
+ svn_string_t *propname;
+ svn_string_t *propval;
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
 
- if (! strcmp (value->data, ""))
- /* The user wants to delete the property. */
- value = NULL;
-
- if (targets->nelts)
- for (i = 0; i < targets->nelts; i++)
- {
- svn_string_t *target = ((svn_string_t **) (targets->elts))[i];
- err = svn_wc_prop_set (name, value, target, pool);
- if (err)
- return err;
-
- /* fitz todo: make these print out only when VERBOSE */
- if (value)
- printf ("property `%s' set on %s.\n",
- name->data, target->data);
- else
- printf ("property `%s' deleted from %s\n",
- name->data, target->data);
- }
- else
+ /* PROPNAME and PROPVAL expected as first 2 arguments */
+ err = svn_cl__parse_num_args (os, opt_state,
+ "propset", 2, pool);
+
+ if (err)
+ return err;
+
+ propname = ((svn_string_t **) (opt_state->args->elts))[0];
+ propval = ((svn_string_t **) (opt_state->args->elts))[1];
+
+ if (! strcmp (propval->data, ""))
+ {
+ /* The user wants to delete the property. */
+ propval = NULL;
+ }
+
+ /* suck up all the remaining arguments into a targets array */
+ targets = svn_cl__args_to_target_array (os, pool);
+
+ /* Add "." if user passed 0 file arguments */
+ svn_cl__push_implicit_dot_target(targets, pool);
+
+ for (i = 0; i < targets->nelts; i++)
     {
- fprintf (stderr, "svn propset: arguments required\n");
- err = svn_cl__help (opt_state, targets, pool);
+ svn_string_t *target = ((svn_string_t **) (targets->elts))[i];
+ err = svn_wc_prop_set (propname, propval, target, pool);
       if (err)
         return err;
+
+ /* fitz todo: make these print out only when VERBOSE */
+ if (propval)
+ printf ("property `%s' set on %s.\n",
+ propname->data, target->data);
+ else
+ printf ("property `%s' deleted from %s\n",
+ propname->data, target->data);
     }
 
   return SVN_NO_ERROR;
Index: status-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/status-cmd.c,v
retrieving revision 1.12
diff -u -r1.12 status-cmd.c
--- status-cmd.c 2001/03/13 01:45:34 1.12
+++ status-cmd.c 2001/03/17 03:05:28
@@ -30,13 +30,16 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__status (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__status (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
   apr_hash_t *statushash;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Index: update-cmd.c
===================================================================
RCS file: /cvs/subversion/subversion/client/update-cmd.c,v
retrieving revision 1.14
diff -u -r1.14 update-cmd.c
--- update-cmd.c 2001/03/13 01:45:34 1.14
+++ update-cmd.c 2001/03/17 03:05:29
@@ -30,12 +30,15 @@
 /*** Code. ***/
 
 svn_error_t *
-svn_cl__update (svn_cl__opt_state_t *opt_state,
- apr_array_header_t *targets,
+svn_cl__update (apr_getopt_t *os,
+ svn_cl__opt_state_t *opt_state,
                 apr_pool_t *pool)
 {
   svn_error_t *err;
+ apr_array_header_t *targets;
   int i;
+
+ targets = svn_cl__args_to_target_array (os, pool);
 
   /* Add "." if user passed 0 arguments */
   svn_cl__push_implicit_dot_target(targets, pool);
Received on Sat Oct 21 14:36:26 2006

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