Skip to content

Commit 02cac16

Browse files
committed
svnbrowse: Use the Subversion framework for command-line argument
parsing and help. * subversion/svnbrowse/svnbrowse.c (includes): Add svn_cmdline.h, private/svn_cmdline_private.h, and svn_private_config. (opt_): New enum. (svn_browse__opt_state_t): New struct. (svn_browse__options): List all options. (show_usage, show_help, show_version): New functions. (sub_main, main): Mark argv as const. (sub_main): Parse command line using getopt, and add some boilerplate-ish stolen initialization code; the majority of options are no-op. git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1932856 13f79535-47bb-0310-9956-ffa450edef68
1 parent cbfaf22 commit 02cac16

1 file changed

Lines changed: 269 additions & 6 deletions

File tree

subversion/svnbrowse/svnbrowse.c

Lines changed: 269 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <apr.h>
2626

27+
#include "svn_cmdline.h"
2728
#include "svn_client.h"
2829
#include "svn_opt.h"
2930
#include "svn_ra.h"
@@ -32,8 +33,112 @@
3233
#include "svn_cmdline.h"
3334
#include "svn_error.h"
3435

36+
#include "private/svn_cmdline_private.h"
37+
3538
#include <ncurses.h>
3639

40+
#include "svn_private_config.h"
41+
42+
enum {
43+
opt_auth_password = SVN_OPT_FIRST_LONGOPT_ID,
44+
opt_auth_password_from_stdin,
45+
opt_auth_username,
46+
opt_config_dir,
47+
opt_config_option,
48+
opt_no_auth_cache,
49+
opt_version,
50+
opt_trust_server_cert,
51+
opt_trust_server_cert_failures,
52+
opt_password_from_stdin,
53+
};
54+
55+
typedef struct svn_browse__opt_state_t {
56+
svn_boolean_t version; /* print version information */
57+
svn_boolean_t verbose; /* for svnbrowse --version */
58+
svn_boolean_t quiet; /* for svnbrowse --version */
59+
svn_boolean_t help; /* print usage message */
60+
61+
const char *auth_username; /* auth username */
62+
const char *auth_password; /* auth password */
63+
apr_array_header_t *targets; /* target list from file */
64+
svn_boolean_t no_auth_cache; /* do not cache authentication information */
65+
const char *config_dir; /* over-riding configuration directory */
66+
apr_array_header_t *config_options; /* over-riding configuration options */
67+
svn_opt_revision_t revision;
68+
69+
/* trust server SSL certs that would otherwise be rejected as "untrusted" */
70+
svn_boolean_t trust_server_cert_unknown_ca;
71+
svn_boolean_t trust_server_cert_cn_mismatch;
72+
svn_boolean_t trust_server_cert_expired;
73+
svn_boolean_t trust_server_cert_not_yet_valid;
74+
svn_boolean_t trust_server_cert_other_failure;
75+
} svn_browse__opt_state_t;
76+
77+
/* Option codes and descriptions for the command line client.
78+
* The entire list must be terminated with an entry of nulls. */
79+
const apr_getopt_option_t svn_browse__options[] =
80+
{
81+
{"username", opt_auth_username, 1, N_("specify a username ARG")},
82+
{"password", opt_auth_password, 1,
83+
N_("specify a password ARG (caution: on many operating\n"
84+
" "
85+
"systems, other users will be able to see this)")},
86+
{"password-from-stdin",
87+
opt_auth_password_from_stdin, 0,
88+
N_("read password from stdin")},
89+
{"revision", 'r', 1,
90+
N_("ARG\n"
91+
" "
92+
"A revision argument can be one of:\n"
93+
" "
94+
" NUMBER revision number\n"
95+
" "
96+
" '{' DATE '}' revision at start of the date\n"
97+
" "
98+
" 'HEAD' latest in repository\n"
99+
" "
100+
" 'BASE' base rev of item's working copy\n"
101+
" "
102+
" 'COMMITTED' last commit at or before BASE\n"
103+
" "
104+
" 'PREV' revision just before COMMITTED")},
105+
{"help", 'h', 0, N_("show help on a subcommand")},
106+
{NULL, '?', 0, N_("show help on a subcommand")},
107+
{"trust-server-cert", opt_trust_server_cert, 0,
108+
N_("deprecated; same as\n"
109+
" "
110+
"--trust-server-cert-failures=unknown-ca")},
111+
{"trust-server-cert-failures", opt_trust_server_cert_failures, 1,
112+
N_("with --non-interactive, accept SSL server\n"
113+
" "
114+
"certificates with failures; ARG is comma-separated\n"
115+
" "
116+
"list of 'unknown-ca' (Unknown Authority),\n"
117+
" "
118+
"'cn-mismatch' (Hostname mismatch), 'expired'\n"
119+
" "
120+
"(Expired certificate), 'not-yet-valid' (Not yet\n"
121+
" "
122+
"valid certificate) and 'other' (all other not\n"
123+
" "
124+
"separately classified certificate errors).")},
125+
{"config-dir", opt_config_dir, 1,
126+
N_("read user configuration files from directory ARG")},
127+
{"config-option", opt_config_option, 1,
128+
N_("set user configuration option in the format:\n"
129+
" "
130+
" FILE:SECTION:OPTION=[VALUE]\n"
131+
" "
132+
"For example:\n"
133+
" "
134+
" servers:global:http-library=serf")},
135+
{"no-auth-cache", opt_no_auth_cache, 0,
136+
N_("do not cache authentication tokens")},
137+
{"version", opt_version, 0, N_("show program version information")},
138+
{"verbose", 'v', 0, N_("print extra information")},
139+
{ NULL, 0, 0, NULL }
140+
};
141+
37142
/* Control+ASCII character are represented as values 1-26 according to their
38143
* alphabetical order. */
39144
#define CTRL(ch) ((ch) - 'a' + 1)
@@ -258,18 +363,176 @@ view_draw(svn_browse__view_t *view, apr_pool_t *pool)
258363
}
259364

260365
static svn_error_t *
261-
sub_main(int *code, int argc, char *argv[], apr_pool_t *pool)
366+
show_usage(apr_pool_t *scratch_pool)
367+
{
368+
fprintf(stderr, "Type 'svnbrowse --help' for usage.\n");
369+
return SVN_NO_ERROR;
370+
}
371+
372+
static svn_error_t *
373+
show_help(apr_pool_t *scratch_pool,
374+
const svn_browse__opt_state_t *opt_state)
375+
{
376+
svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool);
377+
const apr_getopt_option_t *opt;
378+
379+
svn_stringbuf_appendcstr(buf, N_(
380+
"usage: svnbrowse <target> [options]\n"
381+
"Interactively browse Subversion repositories\n"
382+
"\n"
383+
));
384+
385+
svn_stringbuf_appendcstr(buf, N_("Valid options:\n"));
386+
for (opt = svn_browse__options; opt->description; opt++)
387+
{
388+
if (opt->name)
389+
{
390+
const char *opts;
391+
svn_opt_format_option(&opts, opt, TRUE /* doc */, scratch_pool);
392+
svn_stringbuf_appendcstr(buf, " ");
393+
svn_stringbuf_appendcstr(buf, opts);
394+
svn_stringbuf_appendbyte(buf, '\n');
395+
}
396+
}
397+
398+
return svn_error_trace(svn_cmdline_fputs(buf->data, stderr, scratch_pool));
399+
}
400+
401+
static svn_error_t *
402+
show_version(apr_pool_t *scratch_pool,
403+
const svn_browse__opt_state_t *opt_state)
404+
{
405+
const char *ra_desc_start
406+
= "The following repository access (RA) modules are available:\n\n";
407+
svn_stringbuf_t *version_footer;
408+
409+
version_footer = svn_stringbuf_create(ra_desc_start, scratch_pool);
410+
SVN_ERR(svn_ra_print_modules(version_footer, scratch_pool));
411+
412+
SVN_ERR(svn_opt_print_help5(NULL,
413+
"svnbrowse",
414+
TRUE /* print_version */,
415+
opt_state->quiet,
416+
opt_state->verbose,
417+
version_footer->data,
418+
NULL, NULL, NULL, NULL, NULL,
419+
scratch_pool));
420+
421+
return SVN_NO_ERROR;
422+
}
423+
424+
static svn_error_t *
425+
sub_main(int *code, int argc, const char *argv[], apr_pool_t *pool)
262426
{
263427
const char *url;
264428
svn_browse__model_t *ctx;
265429
svn_browse__view_t *view;
430+
svn_browse__opt_state_t opt_state = { 0 };
431+
svn_boolean_t read_pass_from_stdin = FALSE;
266432
apr_pool_t *iterpool;
433+
apr_getopt_t *os;
434+
435+
opt_state.revision.kind = svn_opt_revision_head;
436+
opt_state.config_options =
437+
apr_array_make(pool, 0, sizeof(svn_cmdline__config_argument_t *));
438+
439+
SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));
440+
os->interleave = 1;
441+
442+
while (TRUE)
443+
{
444+
const char *opt_arg;
445+
int opt_id;
446+
447+
/* Parse the next option. */
448+
apr_status_t status = apr_getopt_long(os, svn_browse__options, &opt_id,
449+
&opt_arg);
450+
451+
if (APR_STATUS_IS_EOF(status))
452+
break;
453+
else if (status != APR_SUCCESS)
454+
{
455+
SVN_ERR(show_usage(pool));
456+
*code = EXIT_FAILURE;
457+
return SVN_NO_ERROR;
458+
}
459+
460+
switch (opt_id)
461+
{
462+
case 'r':
463+
SVN_ERR(svn_opt_parse_one_revision(&opt_state.revision, opt_arg, pool));
464+
break;
465+
case 'h':
466+
case '?':
467+
opt_state.help = TRUE;
468+
break;
469+
case opt_version:
470+
opt_state.version = TRUE;
471+
break;
472+
case opt_auth_username:
473+
opt_state.auth_username = apr_pstrdup(pool, opt_arg);
474+
break;
475+
case opt_auth_password:
476+
opt_state.auth_password = apr_pstrdup(pool, opt_arg);
477+
break;
478+
case opt_auth_password_from_stdin:
479+
read_pass_from_stdin = TRUE;
480+
break;
481+
case opt_no_auth_cache:
482+
opt_state.no_auth_cache = TRUE;
483+
break;
484+
/* ### can we drop it in a 1.16 tool? */
485+
case opt_trust_server_cert: /* backwards compat to 1.8 */
486+
opt_state.trust_server_cert_unknown_ca = TRUE;
487+
break;
488+
case opt_trust_server_cert_failures:
489+
SVN_ERR(svn_cmdline__parse_trust_options(
490+
&opt_state.trust_server_cert_unknown_ca,
491+
&opt_state.trust_server_cert_cn_mismatch,
492+
&opt_state.trust_server_cert_expired,
493+
&opt_state.trust_server_cert_not_yet_valid,
494+
&opt_state.trust_server_cert_other_failure,
495+
opt_arg, pool));
496+
break;
497+
case opt_config_dir:
498+
opt_state.config_dir = svn_dirent_internal_style(opt_arg, pool);
499+
break;
500+
case opt_config_option:
501+
SVN_ERR(svn_cmdline__parse_config_option(opt_state.config_options,
502+
opt_arg, "svnbrowse: ", pool));
503+
break;
504+
default:
505+
break;
506+
}
507+
}
267508

268-
if (argc != 2)
269-
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
270-
"usage: svnbrowse <URL>");
509+
if (opt_state.version)
510+
{
511+
SVN_ERR(show_version(pool, &opt_state));
512+
return SVN_NO_ERROR;
513+
}
514+
515+
if (opt_state.help)
516+
{
517+
SVN_ERR(show_help(pool, &opt_state));
518+
return SVN_NO_ERROR;
519+
}
520+
521+
/* TODO: WC paths are not implemented; svn_uri_canonicalize_safe() will just
522+
* fail in case of one */
523+
url = (os->ind < argc) ? os->argv[os->ind++] : ".";
524+
SVN_ERR(svn_uri_canonicalize_safe(&url, NULL, url, pool, pool));
525+
526+
/* we must fail if there are extra arguments */
527+
if (os->ind < argc - 1)
528+
{
529+
printf("%d\n", os->ind);
530+
*code = EXIT_FAILURE;
531+
SVN_ERR(show_usage(pool));
532+
return SVN_NO_ERROR;
533+
}
271534

272-
SVN_ERR(svn_uri_canonicalize_safe(&url, NULL, argv[1], pool, pool));
535+
SVN_ERR(svn_config_ensure(opt_state.config_dir, pool));
273536

274537
SVN_ERR(model_create(&ctx, url, SVN_INVALID_REVNUM, pool, pool));
275538

@@ -337,7 +600,7 @@ sub_main(int *code, int argc, char *argv[], apr_pool_t *pool)
337600
return SVN_NO_ERROR;
338601
}
339602

340-
int main(int argc, char *argv[])
603+
int main(int argc, const char *argv[])
341604
{
342605
apr_pool_t *pool;
343606
int exit_code = EXIT_SUCCESS;

0 commit comments

Comments
 (0)