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

Tabular command processing sans AutoOpts

From: Bruce Korb <bkorb_at_cruzio.com>
Date: 2000-11-12 23:03:19 CET

This patch will make the command line client become table driven.
The option processing remains the (slightly) renamed parse_options
procedure (svn_cl__parse_options), though we all know this method
won't scale. :-) This patch will also add a bazillion stubs:

diff -u -r1.7 Makefile.am
--- Makefile.am 2000/10/26 20:02:16 1.7
+++ Makefile.am 2000/11/12 20:45:36
@@ -1,7 +1,11 @@
 ## Makefile.in is generated from this by automake.
 
 noinst_PROGRAMS = svn
-svn_SOURCES = main.c status.c trace.c
+svn_SOURCES = main.c status.c trace.c \
+ ad-cmd.c an-cmd.c ap-cmd.c br-cmd.c ci-cmd.c co-cmd.c cp-cmd.c \
+ cv-cmd.c df-cmd.c im-cmd.c lo-cmd.c me-cmd.c mv-cmd.c pg-cmd.c \
+ ps-cmd.c rd-cmd.c rm-cmd.c rp-cmd.c st-cmd.c sy-cmd.c tg-cmd.c \
+ up-cmd.c
 
that were all mechanically generated. The implemented commands were
patched with the working code from main.c. "make check" passes.

Index: Makefile.am
===================================================================
RCS file: /cvs/subversion/subversion/client/Makefile.am,v
retrieving revision 1.7
diff -u -r1.7 Makefile.am
--- Makefile.am 2000/10/26 20:02:16 1.7
+++ Makefile.am 2000/11/12 20:45:36
@@ -1,7 +1,11 @@
 ## Makefile.in is generated from this by automake.
 
 noinst_PROGRAMS = svn
-svn_SOURCES = main.c status.c trace.c
+svn_SOURCES = main.c status.c trace.c \
+ ad-cmd.c an-cmd.c ap-cmd.c br-cmd.c ci-cmd.c co-cmd.c cp-cmd.c \
+ cv-cmd.c df-cmd.c im-cmd.c lo-cmd.c me-cmd.c mv-cmd.c pg-cmd.c \
+ ps-cmd.c rd-cmd.c rm-cmd.c rp-cmd.c st-cmd.c sy-cmd.c tg-cmd.c \
+ up-cmd.c
 
 ## Flags needed when compiling:
 INCLUDES = -I. -I../include -I../../apr/include -I../../expat-lite
Index: cl.h
===================================================================
RCS file: /cvs/subversion/subversion/client/cl.h,v
retrieving revision 1.8
diff -u -r1.8 cl.h
--- cl.h 2000/11/12 18:08:55 1.8
+++ cl.h 2000/11/12 20:45:37
@@ -58,7 +58,72 @@
 #include "svn_wc.h"
 #include "svn_string.h"
 
+/* All client command procedures conform to this prototype */
+typedef svn_error_t * (t_cl_cmd_proc) (int argc, char** argv, apr_pool_t*);
 
+/* Structure type for the command dispatch table.
+ tOptions is a place-holder */
+typedef void tOptions;
+
+typedef struct {
+ const char *cmd_name;
+ size_t name_len;
+ svn_boolean_t fork_first;
+ t_cl_cmd_proc *cmd_func;
+ tOptions* cmd_opts;
+} t_cmd_desc;
+
+typedef enum {
+ NULL_COMMAND = 0,
+ ADD_COMMAND,
+ ANNOTATE_COMMAND,
+ APPLY_COMMAND,
+ BRANCH_COMMAND,
+ COMMIT_COMMAND,
+ CHECKOUT_COMMAND,
+ COPY_COMMAND,
+ CONVERT_COMMAND,
+ DIFF_COMMAND,
+ IMPORT_COMMAND,
+ LOG_COMMAND,
+ MERGE_COMMAND,
+ RENAME_COMMAND,
+ PROPGET_COMMAND,
+ PROPSET_COMMAND,
+ RDIFF_COMMAND,
+ DELETE_COMMAND,
+ REPO_COMMAND,
+ STATUS_COMMAND,
+ SYNC_COMMAND,
+ TAG_COMMAND,
+ UPDATE_COMMAND
+} te_command;
+
+t_cl_cmd_proc
+ svn_cl__add,
+ svn_cl__annotate,
+ svn_cl__apply,
+ svn_cl__branch,
+ svn_cl__commit,
+ svn_cl__checkout,
+ svn_cl__copy,
+ svn_cl__convert,
+ svn_cl__diff,
+ svn_cl__help,
+ svn_cl__import,
+ svn_cl__log,
+ svn_cl__merge,
+ svn_cl__rename,
+ svn_cl__propget,
+ svn_cl__propset,
+ svn_cl__rdiff,
+ svn_cl__delete,
+ svn_cl__repo,
+ svn_cl__status,
+ svn_cl__sync,
+ svn_cl__tag,
+ svn_cl__update;
+
 
 /* Print PATH's status line using STATUS. */
 void svn_cl__print_status (svn_string_t *path, svn_wc_status_t *status);
@@ -75,6 +140,17 @@
                                        svn_string_t *initial_path,
                                        apr_pool_t *pool);
 
+/* Until there is something else, this is it */
+void
+svn_cl__parse_options (int argc,
+ char **argv,
+ te_command command,
+ svn_string_t **xml_file,
+ svn_string_t **target, /* dest_dir or file to add */
+ svn_revnum_t *revision, /* ancestral or new */
+ svn_string_t **ancestor_path,
+ svn_boolean_t *force,
+ apr_pool_t *pool);
 #endif /* SVN_CL_H */
 
 
Index: main.c
===================================================================
RCS file: /cvs/subversion/subversion/client/main.c,v
retrieving revision 1.20
diff -u -r1.20 main.c
--- main.c 2000/11/12 18:06:15 1.20
+++ main.c 2000/11/12 20:45:37
@@ -65,24 +65,66 @@
 
 /*** kff todo: this trace editor will get moved to its own file ***/
 
-
-enum command
-{ checkout_command = 1,
- update_command,
- add_command,
- delete_command,
- commit_command,
- status_command
+static t_cmd_desc cmd_table[] = {
+ { "ad", 2, TRUE, svn_cl__add, NULL },
+ { "add", 3, TRUE, svn_cl__add, NULL },
+ { "an", 2, TRUE, svn_cl__annotate, NULL },
+ { "ann", 3, TRUE, svn_cl__annotate, NULL },
+ { "annotate", 8, TRUE, svn_cl__annotate, NULL },
+ { "ap", 2, TRUE, svn_cl__apply, NULL },
+ { "apply", 5, TRUE, svn_cl__apply, NULL },
+ { "branch", 6, TRUE, svn_cl__branch, NULL },
+ { "checkout", 8, TRUE, svn_cl__checkout, NULL },
+ { "ci", 2, TRUE, svn_cl__commit, NULL },
+ { "co", 2, TRUE, svn_cl__checkout, NULL },
+ { "commit", 6, TRUE, svn_cl__commit, NULL },
+ { "convert", 7, TRUE, svn_cl__convert, NULL },
+ { "copy", 4, TRUE, svn_cl__copy, NULL },
+ { "cp", 2, TRUE, svn_cl__copy, NULL },
+ { "cv", 2, TRUE, svn_cl__convert, NULL },
+ { "del", 3, TRUE, svn_cl__delete, NULL },
+ { "delete", 6, TRUE, svn_cl__delete, NULL },
+ { "di", 2, TRUE, svn_cl__diff, NULL },
+ { "dif", 3, TRUE, svn_cl__diff, NULL },
+ { "diff", 4, TRUE, svn_cl__diff, NULL },
+ { "help", 4, FALSE, svn_cl__help, NULL },
+ { "im", 2, TRUE, svn_cl__import, NULL },
+ { "imp", 3, TRUE, svn_cl__import, NULL },
+ { "import", 6, TRUE, svn_cl__import, NULL },
+ { "lo", 2, TRUE, svn_cl__log, NULL },
+ { "log", 3, TRUE, svn_cl__log, NULL },
+ { "me", 2, TRUE, svn_cl__merge, NULL },
+ { "merge", 5, TRUE, svn_cl__merge, NULL },
+ { "mv", 2, TRUE, svn_cl__rename, NULL },
+ { "new", 3, TRUE, svn_cl__add, NULL },
+ { "pg", 2, TRUE, svn_cl__propget, NULL },
+ { "pget", 4, TRUE, svn_cl__propget, NULL },
+ { "propget", 7, TRUE, svn_cl__propget, NULL },
+ { "propset", 7, TRUE, svn_cl__propset, NULL },
+ { "ps", 2, TRUE, svn_cl__propset, NULL },
+ { "pset", 4, TRUE, svn_cl__propset, NULL },
+ { "rdiff", 5, TRUE, svn_cl__rdiff, NULL },
+ { "rename", 6, TRUE, svn_cl__rename, NULL },
+ { "repo", 4, TRUE, svn_cl__repo, NULL },
+ { "rm", 2, TRUE, svn_cl__delete, NULL },
+ { "st", 2, TRUE, svn_cl__status, NULL },
+ { "stat", 4, TRUE, svn_cl__status, NULL },
+ { "status", 6, TRUE, svn_cl__status, NULL },
+ { "sy", 2, TRUE, svn_cl__sync, NULL },
+ { "sync", 4, TRUE, svn_cl__sync, NULL },
+ { "tag", 3, TRUE, svn_cl__tag, NULL },
+ { "up", 2, TRUE, svn_cl__update, NULL },
+ { "update", 6, TRUE, svn_cl__update, NULL }
 };
 
 
+
 
 /*** Code. ***/
 
 static void
 parse_command_options (int argc,
                        char **argv,
- int i,
                        char *progname,
                        svn_string_t **xml_file,
                        svn_string_t **target,
@@ -91,7 +133,9 @@
                        svn_boolean_t *force,
                        apr_pool_t *pool)
 {
- for (; i < argc; i++)
+ int i;
+
+ for (i = 0; i < argc; i++)
     {
       if (strcmp (argv[i], "--xml-file") == 0)
         {
@@ -148,10 +192,10 @@
 /* We'll want an off-the-shelf option parsing system soon... too bad
    GNU getopt is out for copyright reasons (?). In the meantime,
    reinvent the wheel: */
-static void
-parse_options (int argc,
+void
+svn_cl__parse_options (int argc,
                char **argv,
- enum command *command,
+ te_command command,
                svn_string_t **xml_file,
                svn_string_t **target, /* dest_dir or file to add */
                svn_revnum_t *revision, /* ancestral or new */
@@ -162,170 +206,140 @@
   char *s = argv[0]; /* svn progname */
   int i;
 
- for (i = 1; i < argc; i++)
- {
- /* todo: do the cvs synonym thing eventually */
- if (strcmp (argv[i], "checkout") == 0)
- {
- *command = checkout_command;
- goto do_command_opts;
- }
- else if (strcmp (argv[i], "update") == 0)
- {
- *command = update_command;
- goto do_command_opts;
- }
- else if (strcmp (argv[i], "add") == 0)
- {
- *command = add_command;
- goto do_command_opts;
- }
- else if (strcmp (argv[i], "delete") == 0)
- {
- *command = delete_command;
- goto do_command_opts;
- }
- else if (strcmp (argv[i], "commit") == 0)
- {
- *command = commit_command;
- goto do_command_opts;
- }
- else if (strcmp (argv[i], "status") == 0)
- {
- *command = status_command;
- goto do_command_opts;
- }
- else
- {
- fprintf (stderr, "%s: unknown or untimely argument \"%s\"\n",
- s, argv[i]);
- exit (1);
- }
- }
-
- do_command_opts:
- parse_command_options (argc, argv, ++i, s,
+ /* Skip the program and subcommand names. Parse the rest. */
+ parse_command_options (argc-2, argv+2, s,
                          xml_file, target, revision, ancestor_path, force,
                          pool);
 
   /* Sanity checks: make sure we got what we needed. */
- if (! *command)
- {
- fprintf (stderr, "%s: no command given\n", s);
- exit (1);
- }
- if ((! *xml_file) && ((*command != add_command)
- && (*command != status_command)
- && (*command != delete_command)))
+ /* Any command may have an xml_file, but ADD, STATUS and DELETE
+ *must* have the xml_file option */
+ if ((! *xml_file)
+ && (command != ADD_COMMAND)
+ && (command != STATUS_COMMAND)
+ && (command != DELETE_COMMAND))
     {
       fprintf (stderr, "%s: need \"--xml-file FILE.XML\"\n", s);
       exit (1);
     }
- if (*force && (*command != delete_command))
+ if (*force && (command != DELETE_COMMAND))
     {
       fprintf (stderr, "%s: \"--force\" meaningless except for delete\n", s);
       exit (1);
     }
- if (((*command == commit_command) && (*revision == SVN_INVALID_REVNUM))
- || ((*command == update_command) && (*revision == SVN_INVALID_REVNUM)))
+ /* COMMIT and UPDATE must have a valid revision */
+ if ((*revision == SVN_INVALID_REVNUM)
+ && ( (command == COMMIT_COMMAND)
+ || (command == UPDATE_COMMAND)))
     {
       fprintf (stderr, "%s: please use \"--revision VER\" "
                "to specify target revision\n", s);
       exit (1);
     }
- if (((*command == checkout_command)
- || (*command == update_command)
- || (*command == commit_command)
- || (*command == status_command))
- && (*target == NULL))
+ /* CHECKOUT, UPDATE, COMMIT and STATUS have a default target */
+ if ((*target == NULL)
+ && ( (command == CHECKOUT_COMMAND)
+ || (command == UPDATE_COMMAND)
+ || (command == COMMIT_COMMAND)
+ || (command == STATUS_COMMAND)))
     *target = svn_string_create (".", pool);
 }
 
 
+svn_error_t *
+svn_cl__help (int argc, char **argv, apr_pool_t* pool)
+{
+ static const char zUsage[] =
+ "What command do you need help with?\n"
+ "You must type the command you need help with along with the `--help'\n"
+ "command line option. Choose from the following commands:\n\n";
+
+ int ix = 0;
+ t_cmd_desc* pCD = cmd_table;
+
+ fputs( zUsage, stdout );
+
+ for (;;)
+ {
+ printf( " %-8s", (pCD++)->cmd_name );
+ if (++ix >= sizeof( cmd_table ) / sizeof( cmd_table[0] ))
+ break;
+ if ((ix % 7) == 0)
+ fputc( '\n', stdout );
+ }
+
+ fputc( '\n', stdout );
+ return NULL;
+}
+
+
+static t_cmd_desc*
+get_cmd_table_entry (char* pz_cmd)
+{
+ int hi = (sizeof( cmd_table )/sizeof( cmd_table[0] )) - 1;
+ int lo = 0;
+ int av, cmp;
+
+ if (pz_cmd == NULL)
+ {
+ fputs( "svn error: no command name provided\n", stderr );
+ (void)svn_cl__help( 0, NULL, NULL );
+ return NULL;
+ }
+
+ /* Regardless of the option chosen, the user gets --help :-) */
+ if (*pz_cmd == '-')
+ {
+ (void)svn_cl__help( 0, NULL, NULL );
+ return NULL;
+ }
+
+ for (;;)
+ {
+ av = (hi + lo) / 2;
+ cmp = strcmp (pz_cmd, cmd_table[av].cmd_name);
+
+ if (cmp == 0)
+ break;
+
+ if (cmp > 0)
+ lo = av + 1;
+ else
+ hi = av - 1;
+
+ if (hi < lo)
+ {
+ fprintf (stderr, "svn error: `%s' is an unknown command\n", pz_cmd);
+ (void)svn_cl__help( 0, NULL, NULL );
+ return NULL;
+ }
+ }
+
+ return cmd_table + av;
+}
+
+
 int
 main (int argc, char **argv)
 {
+ t_cmd_desc* p_cmd = get_cmd_table_entry (argv[1]);
   svn_error_t *err;
   apr_pool_t *pool;
- svn_revnum_t revision = SVN_INVALID_REVNUM;
- svn_string_t *xml_file = NULL;
- svn_string_t *target = NULL;
- svn_string_t *ancestor_path = NULL;
- svn_boolean_t force = 0;
- enum command command = 0;
- const svn_delta_edit_fns_t *trace_editor;
- void *trace_edit_baton;
 
+ if (p_cmd == NULL)
+ return EXIT_FAILURE;
+
   apr_initialize ();
   pool = svn_pool_create (NULL);
-
- parse_options (argc, argv, &command,
- &xml_file, &target, &revision, &ancestor_path, &force,
- pool);
-
- switch (command)
- {
- /* kff todo: can combine checkout and update cases w/ flag */
- case checkout_command:
- {
- err = svn_cl__get_trace_editor (&trace_editor,
- &trace_edit_baton,
- target,
- pool);
- if (err)
- goto handle_error;
- }
- err = svn_client_checkout (NULL, NULL,
- trace_editor,
- trace_edit_baton,
- target, xml_file,
- ancestor_path, revision, pool);
- break;
- case update_command:
- {
- err = svn_cl__get_trace_editor (&trace_editor,
- &trace_edit_baton,
- target,
- pool);
- if (err)
- goto handle_error;
- }
- err = svn_client_update (NULL, NULL,
- trace_editor, trace_edit_baton,
- target, xml_file, revision, pool);
- break;
- case add_command:
- err = svn_client_add (target, pool);
- break;
- case delete_command:
- err = svn_client_delete (target, force, pool);
- break;
- case commit_command:
- err = svn_client_commit (target, xml_file, revision, pool);
- break;
- case status_command:
- {
- apr_hash_t *statushash;
- err = svn_client_status (&statushash, target, pool);
- if (! err)
- svn_cl__print_status_list (statushash, pool);
- break;
- }
- default:
- fprintf (stderr, "no command given");
- exit (1);
- }
 
- handle_error:
+ err = (*p_cmd->cmd_func) (argc, argv, pool);
   if (err)
     svn_handle_error (err, stdout, 0);
 
   apr_destroy_pool (pool);
 
- return 0;
+ return EXIT_SUCCESS;
 }
-
-
 
 /*
  * local variables:

  • application/octet-stream attachment: clcmd.tgz
Received on Sat Oct 21 14:36:14 2006

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

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