|
24 | 24 |
|
25 | 25 | #include <apr.h> |
26 | 26 |
|
| 27 | +#include "svn_cmdline.h" |
27 | 28 | #include "svn_client.h" |
28 | 29 | #include "svn_opt.h" |
29 | 30 | #include "svn_ra.h" |
|
32 | 33 | #include "svn_cmdline.h" |
33 | 34 | #include "svn_error.h" |
34 | 35 |
|
| 36 | +#include "private/svn_cmdline_private.h" |
| 37 | + |
35 | 38 | #include <ncurses.h> |
36 | 39 |
|
| 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 | + |
37 | 142 | /* Control+ASCII character are represented as values 1-26 according to their |
38 | 143 | * alphabetical order. */ |
39 | 144 | #define CTRL(ch) ((ch) - 'a' + 1) |
@@ -258,18 +363,176 @@ view_draw(svn_browse__view_t *view, apr_pool_t *pool) |
258 | 363 | } |
259 | 364 |
|
260 | 365 | 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) |
262 | 426 | { |
263 | 427 | const char *url; |
264 | 428 | svn_browse__model_t *ctx; |
265 | 429 | svn_browse__view_t *view; |
| 430 | + svn_browse__opt_state_t opt_state = { 0 }; |
| 431 | + svn_boolean_t read_pass_from_stdin = FALSE; |
266 | 432 | 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 | + } |
267 | 508 |
|
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 | + } |
271 | 534 |
|
272 | | - SVN_ERR(svn_uri_canonicalize_safe(&url, NULL, argv[1], pool, pool)); |
| 535 | + SVN_ERR(svn_config_ensure(opt_state.config_dir, pool)); |
273 | 536 |
|
274 | 537 | SVN_ERR(model_create(&ctx, url, SVN_INVALID_REVNUM, pool, pool)); |
275 | 538 |
|
@@ -337,7 +600,7 @@ sub_main(int *code, int argc, char *argv[], apr_pool_t *pool) |
337 | 600 | return SVN_NO_ERROR; |
338 | 601 | } |
339 | 602 |
|
340 | | -int main(int argc, char *argv[]) |
| 603 | +int main(int argc, const char *argv[]) |
341 | 604 | { |
342 | 605 | apr_pool_t *pool; |
343 | 606 | int exit_code = EXIT_SUCCESS; |
|
0 commit comments