svn_error_t * svn_cl__eat_peg_revisions(apr_array_header_t **true_targets_p, const apr_array_header_t *targets, apr_pool_t *pool) { int i; apr_array_header_t *true_targets; true_targets = apr_array_make(pool, targets->nelts, sizeof(const char *)); for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); const char *true_target, *peg; SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg, target, pool)); if (peg[0] && peg[1]) return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("'%s': a peg revision is not allowed here"), target); APR_ARRAY_PUSH(true_targets, const char *) = true_target; } SVN_ERR_ASSERT(true_targets_p); *true_targets_p = true_targets; return SVN_NO_ERROR; }
svn_error_t * svn_cl__eat_peg_revisions(apr_array_header_t **true_targets_p, const apr_array_header_t *targets, apr_pool_t *pool) { int i; apr_array_header_t *true_targets; true_targets = apr_array_make(pool, targets->nelts, sizeof(const char *)); for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); const char *true_target; SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, NULL, target, pool)); APR_ARRAY_PUSH(true_targets, const char *) = true_target; } SVN_ERR_ASSERT(true_targets_p); *true_targets_p = true_targets; return SVN_NO_ERROR; }
/* Note: This is substantially copied into svn_client_args_to_target_array() in * order to move to libsvn_client while maintaining backward compatibility. */ svn_error_t * svn_opt__args_to_target_array(apr_array_header_t **targets_p, apr_getopt_t *os, const apr_array_header_t *known_targets, apr_pool_t *pool) { int i; svn_error_t *err = SVN_NO_ERROR; apr_array_header_t *input_targets = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *)); apr_array_header_t *output_targets = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *)); /* Step 1: create a master array of targets that are in UTF-8 encoding, and come from concatenating the targets left by apr_getopt, plus any extra targets (e.g., from the --targets switch.) */ for (; os->ind < os->argc; os->ind++) { /* The apr_getopt targets are still in native encoding. */ const char *raw_target = os->argv[os->ind]; SVN_ERR(svn_utf_cstring_to_utf8 ((const char **) apr_array_push(input_targets), raw_target, pool)); } if (known_targets) { for (i = 0; i < known_targets->nelts; i++) { /* The --targets array have already been converted to UTF-8, because we needed to split up the list with svn_cstring_split. */ const char *utf8_target = APR_ARRAY_IDX(known_targets, i, const char *); APR_ARRAY_PUSH(input_targets, const char *) = utf8_target; } } /* Step 2: process each target. */ for (i = 0; i < input_targets->nelts; i++) { const char *utf8_target = APR_ARRAY_IDX(input_targets, i, const char *); const char *true_target; const char *target; /* after all processing is finished */ const char *peg_rev; /* * This is needed so that the target can be properly canonicalized, * otherwise the canonicalization does not treat a ".@BASE" as a "." * with a BASE peg revision, and it is not canonicalized to "@BASE". * If any peg revision exists, it is appended to the final * canonicalized path or URL. Do not use svn_opt_parse_path() * because the resulting peg revision is a structure that would have * to be converted back into a string. Converting from a string date * to the apr_time_t field in the svn_opt_revision_value_t and back to * a string would not necessarily preserve the exact bytes of the * input date, so its easier just to keep it in string form. */ SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg_rev, utf8_target, pool)); /* URLs and wc-paths get treated differently. */ if (svn_path_is_url(true_target)) { SVN_ERR(svn_opt__arg_canonicalize_url(&true_target, true_target, pool)); } else /* not a url, so treat as a path */ { const char *base_name; SVN_ERR(svn_opt__arg_canonicalize_path(&true_target, true_target, pool)); /* If the target has the same name as a Subversion working copy administrative dir, skip it. */ base_name = svn_dirent_basename(true_target, pool); /* FIXME: The canonical list of administrative directory names is maintained in libsvn_wc/adm_files.c:svn_wc_set_adm_dir(). That list can't be used here, because that use would create a circular dependency between libsvn_wc and libsvn_subr. Make sure changes to the lists are always synchronized! */ if (0 == strcmp(base_name, ".svn") || 0 == strcmp(base_name, "_svn")) { err = svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED, err, _("'%s' ends in a reserved name"), utf8_target); continue; } } target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL); APR_ARRAY_PUSH(output_targets, const char *) = target; } /* kff todo: need to remove redundancies from targets before passing it to the cmd_func. */ *targets_p = output_targets; return err; }
svn_error_t * svn_opt_parse_path(svn_opt_revision_t *rev, const char **truepath, const char *path /* UTF-8! */, apr_pool_t *pool) { const char *peg_rev; SVN_ERR(svn_opt__split_arg_at_peg_revision(truepath, &peg_rev, path, pool)); /* Parse the peg revision, if one was found */ if (strlen(peg_rev)) { int ret; svn_opt_revision_t start_revision, end_revision; end_revision.kind = svn_opt_revision_unspecified; if (peg_rev[1] == '\0') /* looking at empty peg revision */ { ret = 0; start_revision.kind = svn_opt_revision_unspecified; start_revision.value.number = 0; } else /* looking at non-empty peg revision */ { const char *rev_str = &peg_rev[1]; /* URLs get treated differently from wc paths. */ if (svn_path_is_url(path)) { /* URLs are URI-encoded, so we look for dates with URI-encoded delimeters. */ size_t rev_len = strlen(rev_str); if (rev_len > 6 && rev_str[0] == '%' && rev_str[1] == '7' && (rev_str[2] == 'B' || rev_str[2] == 'b') && rev_str[rev_len-3] == '%' && rev_str[rev_len-2] == '7' && (rev_str[rev_len-1] == 'D' || rev_str[rev_len-1] == 'd')) { rev_str = svn_path_uri_decode(rev_str, pool); } } ret = svn_opt_parse_revision(&start_revision, &end_revision, rev_str, pool); } if (ret || end_revision.kind != svn_opt_revision_unspecified) { /* If an svn+ssh URL was used and it contains only one @, * provide an error message that presents a possible solution * to the parsing error (issue #2349). */ if (strncmp(path, "svn+ssh://", 10) == 0) { const char *at; at = strchr(path, '@'); if (at && strrchr(path, '@') == at) return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Syntax error parsing peg revision " "'%s'; did you mean '%s@'?"), &peg_rev[1], path); } return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Syntax error parsing peg revision '%s'"), &peg_rev[1]); } rev->kind = start_revision.kind; rev->value = start_revision.value; } else { /* Didn't find a peg revision. */ rev->kind = svn_opt_revision_unspecified; } return SVN_NO_ERROR; }
svn_error_t * svn_opt_parse_path(svn_opt_revision_t *rev, const char **truepath, const char *path /* UTF-8! */, apr_pool_t *pool) { const char *peg_rev; SVN_ERR(svn_opt__split_arg_at_peg_revision(truepath, &peg_rev, path, pool)); /* Parse the peg revision, if one was found */ if (strlen(peg_rev)) { int ret; svn_opt_revision_t start_revision, end_revision; end_revision.kind = svn_opt_revision_unspecified; if (peg_rev[1] == '\0') /* looking at empty peg revision */ { ret = 0; start_revision.kind = svn_opt_revision_unspecified; } else /* looking at non-empty peg revision */ { const char *rev_str = &peg_rev[1]; /* URLs get treated differently from wc paths. */ if (svn_path_is_url(path)) { /* URLs are URI-encoded, so we look for dates with URI-encoded delimeters. */ int rev_len = strlen(rev_str); if (rev_len > 6 && rev_str[0] == '%' && rev_str[1] == '7' && (rev_str[2] == 'B' || rev_str[2] == 'b') && rev_str[rev_len-3] == '%' && rev_str[rev_len-2] == '7' && (rev_str[rev_len-1] == 'D' || rev_str[rev_len-1] == 'd')) { rev_str = svn_path_uri_decode(rev_str, pool); } } ret = svn_opt_parse_revision(&start_revision, &end_revision, rev_str, pool); } if (ret || end_revision.kind != svn_opt_revision_unspecified) return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Syntax error parsing revision '%s'"), &peg_rev[1]); rev->kind = start_revision.kind; rev->value = start_revision.value; } else { /* Didn't find a peg revision. */ rev->kind = svn_opt_revision_unspecified; } return SVN_NO_ERROR; }
/* Note: This is substantially copied from svn_opt__args_to_target_array() in * order to move to libsvn_client while maintaining backward compatibility. */ svn_error_t * svn_client_args_to_target_array2(apr_array_header_t **targets_p, apr_getopt_t *os, const apr_array_header_t *known_targets, svn_client_ctx_t *ctx, svn_boolean_t keep_last_origpath_on_truepath_collision, apr_pool_t *pool) { int i; svn_boolean_t rel_url_found = FALSE; const char *root_url = NULL; svn_error_t *err = SVN_NO_ERROR; apr_array_header_t *input_targets = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *)); apr_array_header_t *output_targets = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *)); apr_array_header_t *reserved_names = NULL; /* Step 1: create a master array of targets that are in UTF-8 encoding, and come from concatenating the targets left by apr_getopt, plus any extra targets (e.g., from the --targets switch.) If any of the targets are relative urls, then set the rel_url_found flag.*/ for (; os->ind < os->argc; os->ind++) { /* The apr_getopt targets are still in native encoding. */ const char *raw_target = os->argv[os->ind]; const char *utf8_target; SVN_ERR(svn_utf_cstring_to_utf8(&utf8_target, raw_target, pool)); if (arg_is_repos_relative_url(utf8_target)) rel_url_found = TRUE; APR_ARRAY_PUSH(input_targets, const char *) = utf8_target; } if (known_targets) { for (i = 0; i < known_targets->nelts; i++) { /* The --targets array have already been converted to UTF-8, because we needed to split up the list with svn_cstring_split. */ const char *utf8_target = APR_ARRAY_IDX(known_targets, i, const char *); if (arg_is_repos_relative_url(utf8_target)) rel_url_found = TRUE; APR_ARRAY_PUSH(input_targets, const char *) = utf8_target; } } /* Step 2: process each target. */ for (i = 0; i < input_targets->nelts; i++) { const char *utf8_target = APR_ARRAY_IDX(input_targets, i, const char *); /* Relative urls will be canonicalized when they are resolved later in * the function */ if (arg_is_repos_relative_url(utf8_target)) { APR_ARRAY_PUSH(output_targets, const char *) = utf8_target; } else { const char *true_target; const char *peg_rev; const char *target; /* * This is needed so that the target can be properly canonicalized, * otherwise the canonicalization does not treat a ".@BASE" as a "." * with a BASE peg revision, and it is not canonicalized to "@BASE". * If any peg revision exists, it is appended to the final * canonicalized path or URL. Do not use svn_opt_parse_path() * because the resulting peg revision is a structure that would have * to be converted back into a string. Converting from a string date * to the apr_time_t field in the svn_opt_revision_value_t and back to * a string would not necessarily preserve the exact bytes of the * input date, so its easier just to keep it in string form. */ SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg_rev, utf8_target, pool)); /* URLs and wc-paths get treated differently. */ if (svn_path_is_url(true_target)) { SVN_ERR(svn_opt__arg_canonicalize_url(&true_target, true_target, pool)); } else /* not a url, so treat as a path */ { const char *base_name; const char *original_target; original_target = svn_dirent_internal_style(true_target, pool); SVN_ERR(svn_opt__arg_canonicalize_path(&true_target, true_target, pool)); /* There are two situations in which a 'truepath-conversion' (case-canonicalization to on-disk path on case-insensitive filesystem) needs to be undone: 1. If KEEP_LAST_ORIGPATH_ON_TRUEPATH_COLLISION is TRUE, and this is the last target of a 2-element target list, and both targets have the same truepath. */ if (keep_last_origpath_on_truepath_collision && input_targets->nelts == 2 && i == 1 && strcmp(original_target, true_target) != 0) { const char *src_truepath = APR_ARRAY_IDX(output_targets, 0, const char *); if (strcmp(src_truepath, true_target) == 0) true_target = original_target; } /* 2. If there is an exact match in the wc-db without a corresponding on-disk path (e.g. a scheduled-for-delete file only differing in case from an on-disk file). */ if (strcmp(original_target, true_target) != 0) { const char *target_abspath; svn_node_kind_t kind; svn_error_t *err2; SVN_ERR(svn_dirent_get_absolute(&target_abspath, original_target, pool)); err2 = svn_wc_read_kind(&kind, ctx->wc_ctx, target_abspath, FALSE, pool); if (err2 && (err2->apr_err == SVN_ERR_WC_NOT_WORKING_COPY || err2->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)) { svn_error_clear(err2); } else { SVN_ERR(err2); /* We successfully did a lookup in the wc-db. Now see if it's something interesting. */ if (kind == svn_node_file || kind == svn_node_dir) true_target = original_target; } } /* If the target has the same name as a Subversion working copy administrative dir, skip it. */ base_name = svn_dirent_basename(true_target, pool); if (svn_wc_is_adm_dir(base_name, pool)) { if (!reserved_names) reserved_names = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *)); APR_ARRAY_PUSH(reserved_names, const char *) = utf8_target; continue; } } target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL); if (rel_url_found) { /* Later targets have priority over earlier target, I don't know why, see basic_relative_url_multi_repo. */ SVN_ERR(check_root_url_of_target(&root_url, target, ctx, pool)); } APR_ARRAY_PUSH(output_targets, const char *) = target; } } /* Only resolve relative urls if there were some actually found earlier. */ if (rel_url_found) { /* * Use the current directory's root url if one wasn't found using the * arguments. */ if (root_url == NULL) { err = svn_client_root_url_from_path(&root_url, "", ctx, pool); if (err || root_url == NULL) return svn_error_create(SVN_ERR_WC_NOT_WORKING_COPY, err, _("Resolving '^/': no repository root " "found in the target arguments or " "in the current directory")); } *targets_p = apr_array_make(pool, output_targets->nelts, sizeof(const char *)); for (i = 0; i < output_targets->nelts; i++) { const char *target = APR_ARRAY_IDX(output_targets, i, const char *); if (arg_is_repos_relative_url(target)) { const char *abs_target; const char *true_target; const char *peg_rev; SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg_rev, target, pool)); SVN_ERR(resolve_repos_relative_url(&abs_target, true_target, root_url, pool)); SVN_ERR(svn_opt__arg_canonicalize_url(&true_target, abs_target, pool)); target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL); } APR_ARRAY_PUSH(*targets_p, const char *) = target; } } else
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__export(apr_getopt_t *os, void *baton, apr_pool_t *pool) { svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; const char *from, *to; apr_array_header_t *targets; svn_error_t *err; svn_opt_revision_t peg_revision; const char *truefrom; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, ctx, pool)); /* We want exactly 1 or 2 targets for this subcommand. */ if (targets->nelts < 1) return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); if (targets->nelts > 2) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL); /* The first target is the `from' path. */ from = APR_ARRAY_IDX(targets, 0, const char *); /* Get the peg revision if present. */ SVN_ERR(svn_opt_parse_path(&peg_revision, &truefrom, from, pool)); /* If only one target was given, split off the basename to use as the `to' path. Else, a `to' path was supplied. */ if (targets->nelts == 1) to = svn_path_uri_decode(svn_path_basename(truefrom, pool), pool); else to = APR_ARRAY_IDX(targets, 1, const char *); SVN_ERR(svn_opt__split_arg_at_peg_revision(&to, NULL, to, pool)); if (! opt_state->quiet) svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE, TRUE, FALSE, pool); if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_infinity; /* Decode the partially encoded URL and escape all URL unsafe characters. */ if (svn_path_is_url(truefrom)) truefrom = svn_path_uri_encode(svn_path_uri_decode(truefrom, pool), pool); /* Do the export. */ err = svn_client_export4(NULL, truefrom, to, &peg_revision, &(opt_state->start_revision), opt_state->force, opt_state->ignore_externals, opt_state->depth, opt_state->native_eol, ctx, pool); if (err && err->apr_err == SVN_ERR_WC_OBSTRUCTED_UPDATE && !opt_state->force) SVN_ERR_W(err, _("Destination directory exists; please remove " "the directory or use --force to overwrite")); return err; }
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__export(apr_getopt_t *os, void *baton, apr_pool_t *pool) { svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; const char *from, *to; apr_array_header_t *targets; svn_error_t *err; svn_opt_revision_t peg_revision; const char *truefrom; struct svn_cl__check_externals_failed_notify_baton nwb; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, ctx, FALSE, pool)); /* We want exactly 1 or 2 targets for this subcommand. */ if (targets->nelts < 1) return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL); if (targets->nelts > 2) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL); /* The first target is the `from' path. */ from = APR_ARRAY_IDX(targets, 0, const char *); /* Get the peg revision if present. */ SVN_ERR(svn_opt_parse_path(&peg_revision, &truefrom, from, pool)); /* If only one target was given, split off the basename to use as the `to' path. Else, a `to' path was supplied. */ if (targets->nelts == 1) { if (svn_path_is_url(truefrom)) to = svn_uri_basename(truefrom, pool); else to = svn_dirent_basename(truefrom, pool); } else { to = APR_ARRAY_IDX(targets, 1, const char *); if (strcmp("", to) != 0) /* svn_cl__eat_peg_revisions() but only on one target */ SVN_ERR(svn_opt__split_arg_at_peg_revision(&to, NULL, to, pool)); } SVN_ERR(svn_cl__check_target_is_local_path(to)); if (! opt_state->quiet) SVN_ERR(svn_cl__notifier_mark_export(ctx->notify_baton2)); if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_infinity; nwb.wrapped_func = ctx->notify_func2; nwb.wrapped_baton = ctx->notify_baton2; nwb.had_externals_error = FALSE; ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper; ctx->notify_baton2 = &nwb; /* Do the export. */ err = svn_client_export5(NULL, truefrom, to, &peg_revision, &(opt_state->start_revision), opt_state->force, opt_state->ignore_externals, opt_state->ignore_keywords, opt_state->depth, opt_state->native_eol, ctx, pool); if (err && err->apr_err == SVN_ERR_WC_OBSTRUCTED_UPDATE && !opt_state->force) SVN_ERR_W(err, _("Destination directory exists; please remove " "the directory or use --force to overwrite")); if (nwb.had_externals_error) { svn_error_t *externals_err; externals_err = svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS, NULL, _("Failure occurred processing one or " "more externals definitions")); return svn_error_compose_create(externals_err, err); } return svn_error_trace(err); }