| /* svnraisetreeconflict |
| * |
| * This is a crude command line tool that publishes API to create |
| * tree-conflict markings in a working copy. |
| * |
| * To compile this, go to the root of the Subversion source tree and |
| * call `make svnraisetreeconflict'. You will find the executable file |
| * next to this source file. |
| * |
| * If you want to "install" svnraisetreeconflict, you may call |
| * `make install-tools' in the Subversion source tree root. |
| * (Note: This also installs any other installable tools.) |
| * |
| * svnraisetreeconflict cannot be compiled separate from a Subversion |
| * source tree. |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| */ |
| |
| #include "svn_cmdline.h" |
| #include "svn_pools.h" |
| #include "svn_wc.h" |
| #include "svn_utf.h" |
| #include "svn_path.h" |
| #include "svn_opt.h" |
| #include "svn_version.h" |
| |
| #include "private/svn_wc_private.h" |
| #include "private/svn_cmdline_private.h" |
| |
| #include "svn_private_config.h" |
| |
| #define OPT_VERSION SVN_OPT_FIRST_LONGOPT_ID |
| |
| static svn_error_t * |
| version(apr_pool_t *pool) |
| { |
| return svn_opt_print_help4(NULL, "svnraisetreeconflict", TRUE, FALSE, FALSE, |
| NULL, NULL, NULL, NULL, NULL, NULL, pool); |
| } |
| |
| static void |
| usage(apr_pool_t *pool) |
| { |
| svn_error_clear(svn_cmdline_fprintf |
| (stderr, pool, |
| _("Type 'svnraisetreeconflict --help' for usage.\n"))); |
| } |
| |
| /*************************************************************************** |
| * "enum mapping" functions copied from subversion/libsvn_wc/tree_conflicts.c |
| **************************************************************************/ |
| |
| /* A mapping between a string STR and an enumeration value VAL. */ |
| typedef struct enum_mapping_t |
| { |
| const char *str; |
| int val; |
| } enum_mapping_t; |
| |
| /* A map for svn_node_kind_t values. */ |
| static const enum_mapping_t node_kind_map[] = |
| { |
| { "none", svn_node_none }, |
| { "file", svn_node_file }, |
| { "dir", svn_node_dir }, |
| { "unknown", svn_node_unknown }, |
| { NULL, 0 } |
| }; |
| |
| /* A map for svn_wc_operation_t values. */ |
| static const enum_mapping_t operation_map[] = |
| { |
| { "update", svn_wc_operation_update }, |
| { "switch", svn_wc_operation_switch }, |
| { "merge", svn_wc_operation_merge }, |
| { NULL, 0 } |
| }; |
| |
| /* A map for svn_wc_conflict_action_t values. */ |
| static const enum_mapping_t action_map[] = |
| { |
| { "edit", svn_wc_conflict_action_edit }, |
| { "delete", svn_wc_conflict_action_delete }, |
| { "add", svn_wc_conflict_action_add }, |
| { NULL, 0 } |
| }; |
| |
| /* A map for svn_wc_conflict_reason_t values. */ |
| static const enum_mapping_t reason_map[] = |
| { |
| { "edited", svn_wc_conflict_reason_edited }, |
| { "deleted", svn_wc_conflict_reason_deleted }, |
| { "missing", svn_wc_conflict_reason_missing }, |
| { "obstructed", svn_wc_conflict_reason_obstructed }, |
| { "added", svn_wc_conflict_reason_added }, |
| { NULL, 0 } |
| }; |
| |
| /* Parse the enumeration field pointed to by *START into *RESULT as a plain |
| * 'int', using MAP to convert from strings to enumeration values. |
| * In MAP, a null STR field marks the end of the map. |
| * Don't read further than END. |
| * After reading, make *START point to the character after the field. |
| */ |
| static svn_error_t * |
| read_enum_field(int *result, |
| const enum_mapping_t *map, |
| const char *str, |
| apr_pool_t *pool) |
| { |
| int i; |
| |
| /* Find STR in MAP; error if not found. */ |
| for (i = 0; ; i++) |
| { |
| if (map[i].str == NULL) |
| return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, |
| "Unrecognised parameter value: '%s'", str); |
| if (strcmp(str, map[i].str) == 0) |
| break; |
| } |
| |
| *result = map[i].val; |
| return SVN_NO_ERROR; |
| } |
| |
| static const char* |
| get_enum_str(const enum_mapping_t *map, |
| int enum_val) |
| { |
| int i; |
| for (i = 0; map[i].str != NULL; i++) |
| { |
| if (map[i].val == enum_val) |
| return map[i].str; |
| } |
| return NULL; |
| } |
| |
| static void |
| print_enum_map(const enum_mapping_t *map, |
| apr_pool_t *pool) |
| { |
| int i; |
| for (i = 0; map[i].str != NULL; i++) |
| svn_error_clear(svn_cmdline_fprintf(stdout, pool, |
| " %s", map[i].str)); |
| } |
| |
| static svn_error_t * |
| raise_tree_conflict(int argc, const char **argv, apr_pool_t *pool) |
| { |
| int i = 0; |
| svn_wc_conflict_version_t *left, *right; |
| svn_wc_conflict_description2_t *c; |
| svn_wc_context_t *wc_ctx; |
| |
| /* Conflict description parameters */ |
| const char *wc_path, *wc_abspath; |
| const char *repos_url1, *repos_url2, *path_in_repos1, *path_in_repos2; |
| int operation, action, reason; |
| long peg_rev1, peg_rev2; |
| int kind, kind1, kind2; |
| |
| if (argc != 13) |
| return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, |
| "Wrong number of arguments"); |
| |
| /* Read the parameters */ |
| wc_path = svn_dirent_internal_style(argv[i++], pool); |
| SVN_ERR(read_enum_field(&kind, node_kind_map, argv[i++], pool)); |
| SVN_ERR(read_enum_field(&operation, operation_map, argv[i++], pool)); |
| SVN_ERR(read_enum_field(&action, action_map, argv[i++], pool)); |
| SVN_ERR(read_enum_field(&reason, reason_map, argv[i++], pool)); |
| repos_url1 = argv[i++]; |
| path_in_repos1 = argv[i++]; |
| peg_rev1 = atol(argv[i++]); |
| SVN_ERR(read_enum_field(&kind1, node_kind_map, argv[i++], pool)); |
| repos_url2 = argv[i++]; |
| path_in_repos2 = argv[i++]; |
| peg_rev2 = atol(argv[i++]); |
| SVN_ERR(read_enum_field(&kind2, node_kind_map, argv[i++], pool)); |
| |
| |
| /* Allocate and fill in the description data structures */ |
| SVN_ERR(svn_dirent_get_absolute(&wc_abspath, wc_path, pool)); |
| left = svn_wc_conflict_version_create2(repos_url1, NULL, path_in_repos1, |
| peg_rev1, kind1, pool); |
| right = svn_wc_conflict_version_create2(repos_url2, NULL, path_in_repos2, |
| peg_rev2, kind2, pool); |
| c = svn_wc_conflict_description_create_tree2(wc_abspath, kind, |
| operation, left, right, pool); |
| c->action = (svn_wc_conflict_action_t)action; |
| c->reason = (svn_wc_conflict_reason_t)reason; |
| |
| /* Raise the conflict */ |
| SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool)); |
| SVN_ERR(svn_wc__add_tree_conflict(wc_ctx, c, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static void |
| help(const apr_getopt_option_t *options, apr_pool_t *pool) |
| { |
| svn_error_clear |
| (svn_cmdline_fprintf |
| (stdout, pool, |
| _("usage: svnraisetreeconflict [OPTIONS] WC_PATH NODE_KIND OPERATION ACTION REASON REPOS_URL1 PATH_IN_REPOS1 PEG_REV1 NODE_KIND1 REPOS_URL2 PATH_IN_REPOS2 PEG_REV2 NODE_KIND2\n\n" |
| " Mark the working-copy node WC_PATH as being the victim of a tree conflict.\n" |
| "\n" |
| " WC_PATH's parent directory must be a working copy, otherwise a\n" |
| " tree conflict cannot be raised.\n" |
| "\n" |
| "Valid options:\n"))); |
| while (options->description) |
| { |
| const char *optstr; |
| svn_opt_format_option(&optstr, options, TRUE, pool); |
| svn_error_clear(svn_cmdline_fprintf(stdout, pool, " %s\n", optstr)); |
| ++options; |
| } |
| svn_error_clear(svn_cmdline_fprintf(stdout, pool, |
| _("\n" |
| "Valid enum argument values:\n" |
| " NODE_KIND, NODE_KIND1, NODE_KIND2:\n" |
| " "))); |
| print_enum_map(node_kind_map, pool); |
| svn_error_clear(svn_cmdline_fprintf(stdout, pool, |
| _("\n" |
| " OPERATION:\n" |
| " "))); |
| print_enum_map(operation_map, pool); |
| svn_error_clear(svn_cmdline_fprintf(stdout, pool, |
| _("\n" |
| " ACTION (what svn tried to do):\n" |
| " "))); |
| print_enum_map(action_map, pool); |
| svn_error_clear(svn_cmdline_fprintf(stdout, pool, |
| _("\n" |
| " REASON (what local change made svn fail):\n" |
| " "))); |
| print_enum_map(reason_map, pool); |
| svn_error_clear(svn_cmdline_fprintf(stdout, pool, |
| _("\n" |
| " REPOS_URL1, REPOS_URL2:\n" |
| " The URL of the repository itself, e.g.: file://usr/repos\n" |
| " PATH_IN_REPOS1, PATH_IN_REPOS2:\n" |
| " The complete path of the node in the repository, e.g.: sub/dir/foo\n" |
| " PEG_REV1, PEG_REV2:\n" |
| " The revision number at which the given path is relevant.\n" |
| "\n" |
| "Example:\n" |
| " svnraisetreeconflict ./foo %s %s %s %s file://usr/repos sub/dir/foo 1 %s file://usr/repos sub/dir/foo 3 %s\n\n"), |
| get_enum_str(node_kind_map, svn_node_file), |
| get_enum_str(operation_map, svn_wc_operation_update), |
| get_enum_str(action_map, svn_wc_conflict_action_delete), |
| get_enum_str(reason_map, svn_wc_conflict_reason_deleted), |
| get_enum_str(node_kind_map, svn_node_file), |
| get_enum_str(node_kind_map, svn_node_none) |
| )); |
| } |
| |
| |
| /* Version compatibility check */ |
| static svn_error_t * |
| check_lib_versions(void) |
| { |
| static const svn_version_checklist_t checklist[] = |
| { |
| { "svn_subr", svn_subr_version }, |
| { "svn_wc", svn_wc_version }, |
| { NULL, NULL } |
| }; |
| SVN_VERSION_DEFINE(my_version); |
| |
| return svn_ver_check_list2(&my_version, checklist, svn_ver_equal); |
| } |
| |
| /* |
| * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error, |
| * either return an error to be displayed, or set *EXIT_CODE to non-zero and |
| * return SVN_NO_ERROR. |
| */ |
| static svn_error_t * |
| sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool) |
| { |
| apr_getopt_t *os; |
| const apr_getopt_option_t options[] = |
| { |
| {"help", 'h', 0, N_("display this help")}, |
| {"version", OPT_VERSION, 0, |
| N_("show program version information")}, |
| {0, 0, 0, 0} |
| }; |
| apr_array_header_t *remaining_argv; |
| |
| /* Check library versions */ |
| SVN_ERR(check_lib_versions()); |
| |
| #if defined(WIN32) || defined(__CYGWIN__) |
| /* Set the working copy administrative directory name. */ |
| if (getenv("SVN_ASP_DOT_NET_HACK")) |
| { |
| SVN_ERR(svn_wc_set_adm_dir("_svn", pool)); |
| } |
| #endif |
| |
| SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool)); |
| |
| os->interleave = 1; |
| while (1) |
| { |
| int opt; |
| const char *arg; |
| apr_status_t status = apr_getopt_long(os, options, &opt, &arg); |
| if (APR_STATUS_IS_EOF(status)) |
| break; |
| if (status != APR_SUCCESS) |
| { |
| usage(pool); |
| *exit_code = EXIT_FAILURE; |
| return SVN_NO_ERROR; |
| } |
| |
| switch (opt) |
| { |
| case 'h': |
| help(options, pool); |
| return SVN_NO_ERROR; |
| case OPT_VERSION: |
| SVN_ERR(version(pool)); |
| return SVN_NO_ERROR; |
| default: |
| usage(pool); |
| *exit_code = EXIT_FAILURE; |
| return SVN_NO_ERROR; |
| } |
| } |
| |
| /* Convert the remaining arguments to UTF-8. */ |
| remaining_argv = apr_array_make(pool, 0, sizeof(const char *)); |
| while (os->ind < argc) |
| { |
| const char *s; |
| |
| SVN_ERR(svn_utf_cstring_to_utf8(&s, os->argv[os->ind++], pool)); |
| APR_ARRAY_PUSH(remaining_argv, const char *) = s; |
| } |
| |
| if (remaining_argv->nelts < 1) |
| { |
| usage(pool); |
| *exit_code = EXIT_FAILURE; |
| return SVN_NO_ERROR; |
| } |
| |
| /* Do the main task */ |
| SVN_ERR(raise_tree_conflict(remaining_argv->nelts, |
| (const char **)remaining_argv->elts, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| int |
| main(int argc, const char *argv[]) |
| { |
| apr_pool_t *pool; |
| int exit_code = EXIT_SUCCESS; |
| svn_error_t *err; |
| |
| /* Initialize the app. */ |
| if (svn_cmdline_init("svnraisetreeconflict", stderr) != EXIT_SUCCESS) |
| return EXIT_FAILURE; |
| |
| /* Create our top-level pool. Use a separate mutexless allocator, |
| * given this application is single threaded. |
| */ |
| pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE)); |
| |
| err = sub_main(&exit_code, argc, argv, pool); |
| |
| /* Flush stdout and report if it fails. It would be flushed on exit anyway |
| but this makes sure that output is not silently lost if it fails. */ |
| err = svn_error_compose_create(err, svn_cmdline_fflush(stdout)); |
| |
| if (err) |
| { |
| exit_code = EXIT_FAILURE; |
| svn_cmdline_handle_exit_error(err, NULL, "svnraisetreeconflict: "); |
| } |
| |
| svn_pool_destroy(pool); |
| return exit_code; |
| } |