diff options
Diffstat (limited to 'tools/dev/svnraisetreeconflict/svnraisetreeconflict.c')
-rw-r--r-- | tools/dev/svnraisetreeconflict/svnraisetreeconflict.c | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c b/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c new file mode 100644 index 0000000..aa39816 --- /dev/null +++ b/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c @@ -0,0 +1,410 @@ +/* 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 + +/** A statement macro, similar to @c SVN_INT_ERR, but issues a + * message saying "svnraisetreeconflict:" instead of "svn:". + * + * Evaluate @a expr. If it yields an error, handle that error and + * return @c EXIT_FAILURE. + */ +#define SVNRAISETC_INT_ERR(expr) \ + do { \ + svn_error_t *svn_err__temp = (expr); \ + if (svn_err__temp) { \ + svn_handle_error2(svn_err__temp, stderr, FALSE, \ + "svnraisetreeconflict: "); \ + svn_error_clear(svn_err__temp); \ + return EXIT_FAILURE; } \ + } while (0) + +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"))); + exit(1); +} + +/*************************************************************************** + * "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) + )); + exit(0); +} + + +/* 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_list(&my_version, checklist); +} + +int +main(int argc, const char *argv[]) +{ + apr_pool_t *pool; + svn_error_t *err; + 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; + + /* 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)); + + /* Check library versions */ + err = check_lib_versions(); + if (err) + return svn_cmdline_handle_exit_error(err, pool, "svnraisetreeconflict: "); + +#if defined(WIN32) || defined(__CYGWIN__) + /* Set the working copy administrative directory name. */ + if (getenv("SVN_ASP_DOT_NET_HACK")) + { + err = svn_wc_set_adm_dir("_svn", pool); + if (err) + return svn_cmdline_handle_exit_error(err, pool, "svnraisetreeconflict: "); + } +#endif + + err = svn_cmdline__getopt_init(&os, argc, argv, pool); + if (err) + return svn_cmdline_handle_exit_error(err, pool, "svnraisetreeconflict: "); + + 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); /* this will exit() */ + + switch (opt) + { + case 'h': + help(options, pool); + break; + case OPT_VERSION: + SVNRAISETC_INT_ERR(version(pool)); + exit(0); + break; + default: + usage(pool); /* this will exit() */ + } + } + + /* Convert the remaining arguments to UTF-8. */ + remaining_argv = apr_array_make(pool, 0, sizeof(const char *)); + while (os->ind < argc) + { + const char *s; + + SVNRAISETC_INT_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); /* this will exit() */ + + /* Do the main task */ + SVNRAISETC_INT_ERR(raise_tree_conflict(remaining_argv->nelts, + (const char **)remaining_argv->elts, + pool)); + + svn_pool_destroy(pool); + + /* Flush stdout to make sure that the user will see any printing errors. */ + SVNRAISETC_INT_ERR(svn_cmdline_fflush(stdout)); + + return EXIT_SUCCESS; +} |