/* This implements the svn_proplist_receiver_t interface, printing XML to stdout. */ static svn_error_t * proplist_receiver_xml(void *baton, const char *path, apr_hash_t *prop_hash, apr_pool_t *pool) { svn_cl__opt_state_t *opt_state = ((proplist_baton_t *)baton)->opt_state; svn_boolean_t is_url = ((proplist_baton_t *)baton)->is_url; svn_stringbuf_t *sb = NULL; const char *name_local; if (! is_url) name_local = svn_path_local_style(path, pool); else name_local = path; /* "<target ...>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target", "path", name_local, NULL); SVN_ERR(svn_cl__print_xml_prop_hash(&sb, prop_hash, (! opt_state->verbose), pool)); /* "</target>" */ svn_xml_make_close_tag(&sb, pool, "target"); SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout)); return SVN_NO_ERROR; }
static void status_func (void *baton, const char *path, svn_wc_status2_t *status) { struct status_bt *sb = baton; apr_pool_t *pool = ((status_bt *)baton)->pool; print_status (svn_path_local_style (path, pool), sb->detailed, sb->show_last_committed, sb->repos_locks, status, ((status_bt *) baton)->L, pool); }
std::string Path::native() const { if (m_pathIsUrl) { // this converts something like // http://foo/my%20location // to // http://foo/my location return Url::unescape(m_path.c_str()); } else { // On Windows, p://foo/bar will be converted to p:\foo\bar Pool pool; return svn_path_local_style(m_path.c_str(), pool); } }
/* Throw an SVN_ERR_CLIENT_IS_BINARY_FILE error if PROP_DIFFS indicates a binary MIME type. Else, return SVN_NO_ERROR. */ static svn_error_t * check_mimetype(apr_array_header_t *prop_diffs, const char *target, apr_pool_t *pool) { int i; for (i = 0; i < prop_diffs->nelts; ++i) { const svn_prop_t *prop = &APR_ARRAY_IDX(prop_diffs, i, svn_prop_t); if (strcmp(prop->name, SVN_PROP_MIME_TYPE) == 0 && prop->value && svn_mime_type_is_binary(prop->value->data)) return svn_error_createf (SVN_ERR_CLIENT_IS_BINARY_FILE, 0, _("Cannot calculate blame information for binary file '%s'"), svn_path_local_style(target, pool)); } return SVN_NO_ERROR; }
/* Called by status-cmd.c */ svn_error_t * svn_cl__print_status(const char *path, svn_wc_status2_t *status, svn_boolean_t detailed, svn_boolean_t show_last_committed, svn_boolean_t skip_unrecognized, svn_boolean_t repos_locks, apr_pool_t *pool) { if (! status || (skip_unrecognized && ! status->entry) || (status->text_status == svn_wc_status_none && status->repos_text_status == svn_wc_status_none)) return SVN_NO_ERROR; return print_status(svn_path_local_style(path, pool), detailed, show_last_committed, repos_locks, status, pool); }
/* This implements the svn_proplist_receiver_t interface. */ static svn_error_t * proplist_receiver(void *baton, const char *path, apr_hash_t *prop_hash, apr_pool_t *pool) { svn_cl__opt_state_t *opt_state = ((proplist_baton_t *)baton)->opt_state; svn_boolean_t is_url = ((proplist_baton_t *)baton)->is_url; const char *name_local; if (! is_url) name_local = svn_path_local_style(path, pool); else name_local = path; if (!opt_state->quiet) SVN_ERR(svn_cmdline_printf(pool, _("Properties on '%s':\n"), name_local)); return svn_cl__print_prop_hash(prop_hash, (! opt_state->verbose), pool); }
const char * local_style_skip_ancestor(const char *parent_path, const char *path, apr_pool_t *subpool) { const char *relpath = NULL; if (parent_path) { relpath = svn_path_is_child(parent_path, path, pool); if (!relpath) { if (strcmp(path, parent_path) == 0) relpath = "."; else relpath = path; } } return svn_path_local_style(relpath ? relpath : path, subpool); }
/* Schedule directory DIRNAME, and some of the tree under it, for * addition with access baton ADM_ACCESS. DEPTH is the depth at this * point in the descent (it may be changed for recursive calls). * * If DIRNAME (or any item below directory DIRNAME) is already scheduled for * addition, add will fail and return an error unless FORCE is TRUE. * * Files and directories that match ignore patterns will not be added unless * NO_IGNORE is TRUE. * * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow * the user to cancel the operation */ static svn_error_t * add_dir_recursive(const char *dirname, svn_wc_adm_access_t *adm_access, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, svn_client_ctx_t *ctx, apr_pool_t *pool) { apr_dir_t *dir; apr_finfo_t this_entry; svn_error_t *err; apr_pool_t *subpool; apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME; svn_wc_adm_access_t *dir_access; apr_array_header_t *ignores; /* Check cancellation; note that this catches recursive calls too. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Add this directory to revision control. */ err = svn_wc_add2(dirname, adm_access, NULL, SVN_INVALID_REVNUM, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else if (err) return err; SVN_ERR(svn_wc_adm_retrieve(&dir_access, adm_access, dirname, pool)); if (!no_ignore) SVN_ERR(svn_wc_get_ignores(&ignores, ctx->config, dir_access, pool)); subpool = svn_pool_create(pool); SVN_ERR(svn_io_dir_open(&dir, dirname, pool)); /* Read the directory entries one by one and add those things to version control. */ while (1) { const char *fullpath; svn_pool_clear(subpool); err = svn_io_dir_read(&this_entry, flags, dir, subpool); if (err) { /* Check if we're done reading the dir's entries. */ if (APR_STATUS_IS_ENOENT(err->apr_err)) { apr_status_t apr_err; svn_error_clear(err); apr_err = apr_dir_close(dir); if (apr_err) return svn_error_wrap_apr (apr_err, _("Can't close directory '%s'"), svn_path_local_style(dirname, subpool)); break; } else { return svn_error_createf (err->apr_err, err, _("Error during add of '%s'"), svn_path_local_style(dirname, subpool)); } } /* Skip entries for this dir and its parent. */ if (this_entry.name[0] == '.' && (this_entry.name[1] == '\0' || (this_entry.name[1] == '.' && this_entry.name[2] == '\0'))) continue; /* Check cancellation so you can cancel during an * add of a directory with lots of files. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Skip over SVN admin directories. */ if (svn_wc_is_adm_dir(this_entry.name, subpool)) continue; if ((!no_ignore) && svn_wc_match_ignore_list(this_entry.name, ignores, subpool)) continue; /* Construct the full path of the entry. */ fullpath = svn_path_join(dirname, this_entry.name, subpool); /* Recurse on directories; add files; ignore the rest. */ if (this_entry.filetype == APR_DIR && depth >= svn_depth_immediates) { svn_depth_t depth_below_here = depth; if (depth == svn_depth_immediates) depth_below_here = svn_depth_empty; SVN_ERR(add_dir_recursive(fullpath, dir_access, depth_below_here, force, no_ignore, ctx, subpool)); } else if (this_entry.filetype != APR_UNKFILE && this_entry.filetype != APR_DIR && depth >= svn_depth_files) { err = add_file(fullpath, ctx, dir_access, subpool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else if (err) return err; } } /* Opened by svn_wc_add */ SVN_ERR(svn_wc_adm_close(dir_access)); /* Destroy the per-iteration pool. */ svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__propedit(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 *pname, *pname_utf8; apr_array_header_t *args, *targets; int i; /* Validate the input and get the property's name (and a UTF-8 version of that name). */ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool)); pname = APR_ARRAY_IDX(args, 0, const char *); SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool)); if (! svn_prop_name_is_valid(pname_utf8)) return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, _("'%s' is not a valid Subversion property name"), pname_utf8); if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8)) return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("--encoding option applies only to textual" " Subversion-controlled properties")); /* Suck up all the remaining arguments into a targets array */ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, ctx, pool)); if (opt_state->revprop) /* operate on a revprop */ { svn_revnum_t rev; const char *URL; svn_string_t *propval; svn_string_t original_propval; const char *temp_dir; /* Implicit "." is okay for revision properties; it just helps us find the right repository. */ svn_opt_push_implicit_dot_target(targets, pool); SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets, &URL, pool)); /* Fetch the current property. */ SVN_ERR(svn_client_revprop_get(pname_utf8, &propval, URL, &(opt_state->start_revision), &rev, ctx, pool)); if (! propval) { propval = svn_string_create("", pool); /* This is how we signify to svn_client_revprop_set2() that we want it to check that the original value hasn't changed, but that that original value was non-existent: */ original_propval.data = NULL; /* and .len is ignored */ } else { original_propval = *propval; } /* Run the editor on a temporary file which contains the original property value... */ SVN_ERR(svn_io_temp_dir(&temp_dir, pool)); SVN_ERR(svn_cl__edit_string_externally (&propval, NULL, opt_state->editor_cmd, temp_dir, propval, "svn-prop", ctx->config, svn_prop_needs_translation(pname_utf8), opt_state->encoding, pool)); /* ...and re-set the property's value accordingly. */ if (propval) { SVN_ERR(svn_client_revprop_set2(pname_utf8, propval, &original_propval, URL, &(opt_state->start_revision), &rev, opt_state->force, ctx, pool)); SVN_ERR (svn_cmdline_printf (pool, _("Set new value for property '%s' on revision %ld\n"), pname_utf8, rev)); } else { SVN_ERR(svn_cmdline_printf (pool, _("No changes to property '%s' on revision %ld\n"), pname_utf8, rev)); } } else if (opt_state->start_revision.kind != svn_opt_revision_unspecified) { return svn_error_createf (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Cannot specify revision for editing versioned property '%s'"), pname_utf8); } else /* operate on a normal, versioned property (not a revprop) */ { apr_pool_t *subpool = svn_pool_create(pool); /* The customary implicit dot rule has been prone to user error * here. For example, Jon Trowbridge <*****@*****.**> did * * $ svn propedit HACKING * * and then when he closed his editor, he was surprised to see * * Set new value for property 'HACKING' on '' * * ...meaning that the property named 'HACKING' had been set on * the current working directory, with the value taken from the * editor. So we don't do the implicit dot thing anymore; an * explicit target is always required when editing a versioned * property. */ if (targets->nelts == 0) { return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("Explicit target argument required")); } SVN_ERR(svn_opt__eat_peg_revisions(&targets, targets, pool)); /* For each target, edit the property PNAME. */ for (i = 0; i < targets->nelts; i++) { apr_hash_t *props; const char *target = APR_ARRAY_IDX(targets, i, const char *); svn_string_t *propval, *edited_propval; const char *base_dir = target; const char *target_local; svn_wc_adm_access_t *adm_access; const svn_wc_entry_t *entry; svn_opt_revision_t peg_revision; svn_revnum_t base_rev = SVN_INVALID_REVNUM; svn_pool_clear(subpool); SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); /* Propedits can only happen on HEAD or the working copy, so the peg revision can be as unspecified. */ peg_revision.kind = svn_opt_revision_unspecified; /* Fetch the current property. */ SVN_ERR(svn_client_propget3(&props, pname_utf8, target, &peg_revision, &(opt_state->start_revision), &base_rev, svn_depth_empty, NULL, ctx, subpool)); /* Get the property value. */ propval = apr_hash_get(props, target, APR_HASH_KEY_STRING); if (! propval) propval = svn_string_create("", subpool); if (svn_path_is_url(target)) { /* For URLs, put the temporary file in the current directory. */ base_dir = "."; } else { if (opt_state->message || opt_state->filedata || opt_state->revprop_table) { return svn_error_create (SVN_ERR_CL_UNNECESSARY_LOG_MESSAGE, NULL, _("Local, non-commit operations do not take a log message " "or revision properties")); } /* Split the path if it is a file path. */ SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target, FALSE, 0, ctx->cancel_func, ctx->cancel_baton, subpool)); SVN_ERR(svn_wc_entry(&entry, target, adm_access, FALSE, subpool)); if (! entry) return svn_error_createf (SVN_ERR_ENTRY_NOT_FOUND, NULL, _("'%s' does not appear to be a working copy path"), target); if (entry->kind == svn_node_file) svn_path_split(target, &base_dir, NULL, subpool); } /* Run the editor on a temporary file which contains the original property value... */ SVN_ERR(svn_cl__edit_string_externally(&edited_propval, NULL, opt_state->editor_cmd, base_dir, propval, "svn-prop", ctx->config, svn_prop_needs_translation (pname_utf8), opt_state->encoding, subpool)); target_local = svn_path_is_url(target) ? target : svn_path_local_style(target, subpool); /* ...and re-set the property's value accordingly. */ if (edited_propval && !svn_string_compare(propval, edited_propval)) { svn_commit_info_t *commit_info = NULL; svn_error_t *err = SVN_NO_ERROR; svn_cl__check_boolean_prop_val(pname_utf8, edited_propval->data, subpool); if (ctx->log_msg_func3) SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state, NULL, ctx->config, subpool)); err = svn_client_propset3(&commit_info, pname_utf8, edited_propval, target, svn_depth_empty, opt_state->force, base_rev, NULL, opt_state->revprop_table, ctx, subpool); if (ctx->log_msg_func3) SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool)); else if (err) return err; /* Print a message if we successfully committed or if it was just a wc propset (but not if the user aborted an URL propedit). */ if (commit_info || ! svn_path_is_url(target)) SVN_ERR (svn_cmdline_printf (subpool, _("Set new value for property '%s' on '%s'\n"), pname_utf8, target_local)); if (commit_info && ! opt_state->quiet) SVN_ERR(svn_cl__print_commit_info(commit_info, subpool)); } else { SVN_ERR (svn_cmdline_printf (subpool, _("No changes to property '%s' on '%s'\n"), pname_utf8, target_local)); } } svn_pool_destroy(subpool); } return SVN_NO_ERROR; }
static int l_proplist (lua_State *L) { const char *path = (lua_gettop (L) < 1 || lua_isnil (L, 1)) ? "" : luaL_checkstring (L, 1); svn_opt_revision_t peg_revision; svn_opt_revision_t revision; if (lua_gettop (L) < 2 || lua_isnil (L, 2)) { revision.kind = svn_opt_revision_unspecified; } else { revision.kind = svn_opt_revision_number; revision.value.number = lua_tointeger (L, 2); } peg_revision.kind = svn_opt_revision_unspecified; apr_pool_t *pool; svn_error_t *err; svn_client_ctx_t *ctx; init_function (&ctx, &pool, L); path = svn_path_canonicalize (path, pool); apr_array_header_t *props; int is_url = svn_path_is_url (path); err = svn_client_proplist2 (&props, path, &peg_revision, &revision, TRUE, ctx, pool); IF_ERROR_RETURN (err, pool, L); lua_newtable (L); int i; for (i = 0; i < props->nelts; ++i) { const void *key; void *val; svn_client_proplist_item_t *item = ((svn_client_proplist_item_t **)props->elts)[i]; const char *name_local; if (is_url) { name_local = svn_path_local_style (item->node_name->data, pool); } else { name_local = item->node_name->data; } lua_pushstring (L, name_local); lua_newtable (L); apr_hash_index_t *hi; for (hi = apr_hash_first (pool, item->prop_hash); hi; hi = apr_hash_next (hi)) { const char *pname; svn_string_t *pval; apr_hash_this (hi, &key, NULL, &val); pname = key; pval = (svn_string_t *) val; err = svn_cmdline_cstring_from_utf8 (&pname, pname, pool); IF_ERROR_RETURN (err, pool, L); lua_pushstring (L, pval->data); lua_setfield (L, -2, pname); } lua_settable (L, -3); } svn_pool_destroy (pool); return 1; }
svn_error_t * svn_client__get_revision_number(svn_revnum_t *revnum, svn_revnum_t *youngest_rev, svn_ra_session_t *ra_session, const svn_opt_revision_t *revision, const char *path, apr_pool_t *pool) { switch (revision->kind) { case svn_opt_revision_unspecified: *revnum = SVN_INVALID_REVNUM; break; case svn_opt_revision_number: *revnum = revision->value.number; break; case svn_opt_revision_head: /* If our caller provided a value for HEAD that he wants us to use, we'll use it. Otherwise, we have to query the repository (and possible return our fetched value in *YOUNGEST_REV, too). */ if (youngest_rev && SVN_IS_VALID_REVNUM(*youngest_rev)) { *revnum = *youngest_rev; } else { if (! ra_session) return svn_error_create(SVN_ERR_CLIENT_RA_ACCESS_REQUIRED, NULL, NULL); SVN_ERR(svn_ra_get_latest_revnum(ra_session, revnum, pool)); if (youngest_rev) *youngest_rev = *revnum; } break; case svn_opt_revision_committed: case svn_opt_revision_working: case svn_opt_revision_base: case svn_opt_revision_previous: { svn_wc_adm_access_t *adm_access; const svn_wc_entry_t *ent; /* Sanity check. */ if (path == NULL) return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED, NULL, NULL); SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, path, FALSE, 0, NULL, NULL, pool)); SVN_ERR(svn_wc__entry_versioned(&ent, path, adm_access, FALSE, pool)); SVN_ERR(svn_wc_adm_close(adm_access)); if ((revision->kind == svn_opt_revision_base) || (revision->kind == svn_opt_revision_working)) { *revnum = ent->revision; } else { if (! SVN_IS_VALID_REVNUM(ent->cmt_rev)) return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Path '%s' has no committed " "revision"), path); *revnum = ent->cmt_rev; if (revision->kind == svn_opt_revision_previous) (*revnum)--; } } break; case svn_opt_revision_date: /* ### When revision->kind == svn_opt_revision_date, is there an ### optimization such that we can compare ### revision->value->date with the committed-date in the ### entries file (or rather, with some range of which ### committed-date is one endpoint), and sometimes avoid a ### trip over the RA layer? The only optimizations I can ### think of involve examining other entries to build a ### timespan across which committed-revision is known to be ### the head, but it doesn't seem worth it. -kff */ if (! ra_session) return svn_error_create(SVN_ERR_CLIENT_RA_ACCESS_REQUIRED, NULL, NULL); SVN_ERR(svn_ra_get_dated_revision(ra_session, revnum, revision->value.date, pool)); break; default: return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Unrecognized revision type requested for " "'%s'"), svn_path_local_style(path, pool)); } /* Final check -- if our caller provided a youngest revision, and the number we wound up with is younger than that revision, we need to stick to our caller's idea of "youngest". */ if (youngest_rev && SVN_IS_VALID_REVNUM(*youngest_rev) && SVN_IS_VALID_REVNUM(*revnum) && (*revnum > *youngest_rev)) *revnum = *youngest_rev; return SVN_NO_ERROR; }
svn_error_t * svn_config__parse_registry(svn_config_t *cfg, const char *file, svn_boolean_t must_exist, apr_pool_t *pool) { apr_pool_t *subpool; svn_stringbuf_t *section, *option, *value; svn_error_t *svn_err = SVN_NO_ERROR; HKEY base_hkey, hkey; DWORD index; LONG err; if (0 == strncmp(file, SVN_REGISTRY_HKLM, SVN_REGISTRY_HKLM_LEN)) { base_hkey = HKEY_LOCAL_MACHINE; file += SVN_REGISTRY_HKLM_LEN; } else if (0 == strncmp(file, SVN_REGISTRY_HKCU, SVN_REGISTRY_HKCU_LEN)) { base_hkey = HKEY_CURRENT_USER; file += SVN_REGISTRY_HKCU_LEN; } else { return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, "Unrecognised registry path '%s'", svn_path_local_style(file, pool)); } err = RegOpenKeyEx(base_hkey, file, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hkey); if (err != ERROR_SUCCESS) { const int is_enoent = APR_STATUS_IS_ENOENT(APR_FROM_OS_ERROR(err)); if (!is_enoent) return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, "Can't open registry key '%s'", svn_path_local_style(file, pool)); else if (must_exist && is_enoent) return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, "Can't find registry key '%s'", svn_path_local_style(file, pool)); else return SVN_NO_ERROR; } subpool = svn_pool_create(pool); section = svn_stringbuf_create("", subpool); option = svn_stringbuf_create("", subpool); value = svn_stringbuf_create("", subpool); /* The top-level values belong to the [DEFAULT] section */ svn_err = parse_section(cfg, hkey, SVN_CONFIG__DEFAULT_SECTION, option, value); if (svn_err) goto cleanup; /* Now enumerate the rest of the keys. */ svn_stringbuf_ensure(section, SVN_REG_DEFAULT_NAME_SIZE); for (index = 0; ; ++index) { DWORD section_len = section->blocksize; FILETIME last_write_time; HKEY sub_hkey; err = RegEnumKeyEx(hkey, index, section->data, §ion_len, NULL, NULL, NULL, &last_write_time); if (err == ERROR_NO_MORE_ITEMS) break; if (err == ERROR_MORE_DATA) { svn_stringbuf_ensure(section, section_len); err = RegEnumKeyEx(hkey, index, section->data, §ion_len, NULL, NULL, NULL, &last_write_time); } if (err != ERROR_SUCCESS) { svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, "Can't enumerate registry keys"); goto cleanup; } err = RegOpenKeyEx(hkey, section->data, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &sub_hkey); if (err != ERROR_SUCCESS) { svn_err = svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, "Can't open existing subkey"); goto cleanup; } svn_err = parse_section(cfg, sub_hkey, section->data, option, value); RegCloseKey(sub_hkey); if (svn_err) goto cleanup; } cleanup: RegCloseKey(hkey); svn_pool_destroy(subpool); return svn_err; }
/* A callback of type svn_info_receiver_t. Prints svn info in xml mode to standard out */ static svn_error_t * print_info_xml(void *baton, const char *target, const svn_info_t *info, apr_pool_t *pool) { svn_stringbuf_t *sb = svn_stringbuf_create("", pool); const char *rev_str; if (SVN_IS_VALID_REVNUM(info->rev)) rev_str = apr_psprintf(pool, "%ld", info->rev); else rev_str = apr_pstrdup(pool, _("Resource is not under version control.")); /* "<entry ...>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", "path", svn_path_local_style(target, pool), "kind", svn_cl__node_kind_str_xml(info->kind), "revision", rev_str, NULL); svn_cl__xml_tagged_cdata(&sb, pool, "url", info->URL); if (info->repos_root_URL || info->repos_UUID) { /* "<repository>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repository", NULL); /* "<root> xx </root>" */ svn_cl__xml_tagged_cdata(&sb, pool, "root", info->repos_root_URL); /* "<uuid> xx </uuid>" */ svn_cl__xml_tagged_cdata(&sb, pool, "uuid", info->repos_UUID); /* "</repository>" */ svn_xml_make_close_tag(&sb, pool, "repository"); } if (info->has_wc_info) { /* "<wc-info>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "wc-info", NULL); /* "<schedule> xx </schedule>" */ svn_cl__xml_tagged_cdata(&sb, pool, "schedule", schedule_str(info->schedule)); /* "<depth> xx </depth>" */ svn_cl__xml_tagged_cdata(&sb, pool, "depth", svn_depth_to_word(info->depth)); /* "<copy-from-url> xx </copy-from-url>" */ svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-url", info->copyfrom_url); /* "<copy-from-rev> xx </copy-from-rev>" */ if (SVN_IS_VALID_REVNUM(info->copyfrom_rev)) svn_cl__xml_tagged_cdata(&sb, pool, "copy-from-rev", apr_psprintf(pool, "%ld", info->copyfrom_rev)); /* "<text-updated> xx </text-updated>" */ if (info->text_time) svn_cl__xml_tagged_cdata(&sb, pool, "text-updated", svn_time_to_cstring(info->text_time, pool)); /* "<checksum> xx </checksum>" */ svn_cl__xml_tagged_cdata(&sb, pool, "checksum", info->checksum); if (info->changelist) /* "<changelist> xx </changelist>" */ svn_cl__xml_tagged_cdata(&sb, pool, "changelist", info->changelist); /* "</wc-info>" */ svn_xml_make_close_tag(&sb, pool, "wc-info"); } if (info->last_changed_author || SVN_IS_VALID_REVNUM(info->last_changed_rev) || info->last_changed_date) { svn_cl__print_xml_commit(&sb, info->last_changed_rev, info->last_changed_author, svn_time_to_cstring(info->last_changed_date, pool), pool); } if (info->conflict_old || info->conflict_wrk || info->conflict_new || info->prejfile) { /* "<conflict>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "conflict", NULL); /* "<prev-base-file> xx </prev-base-file>" */ svn_cl__xml_tagged_cdata(&sb, pool, "prev-base-file", info->conflict_old); /* "<prev-wc-file> xx </prev-wc-file>" */ svn_cl__xml_tagged_cdata(&sb, pool, "prev-wc-file", info->conflict_wrk); /* "<cur-base-file> xx </cur-base-file>" */ svn_cl__xml_tagged_cdata(&sb, pool, "cur-base-file", info->conflict_new); /* "<prop-file> xx </prop-file>" */ svn_cl__xml_tagged_cdata(&sb, pool, "prop-file", info->prejfile); /* "</conflict>" */ svn_xml_make_close_tag(&sb, pool, "conflict"); } if (info->lock) { /* "<lock>" */ svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "lock", NULL); /* "<token> xx </token>" */ svn_cl__xml_tagged_cdata(&sb, pool, "token", info->lock->token); /* "<owner> xx </owner>" */ svn_cl__xml_tagged_cdata(&sb, pool, "owner", info->lock->owner); /* "<comment ...> xxxx </comment>" */ svn_cl__xml_tagged_cdata(&sb, pool, "comment", info->lock->comment); /* "<created> xx </created>" */ svn_cl__xml_tagged_cdata(&sb, pool, "created", svn_time_to_cstring (info->lock->creation_date, pool)); /* "<expires> xx </expires>" */ svn_cl__xml_tagged_cdata(&sb, pool, "expires", svn_time_to_cstring (info->lock->expiration_date, pool)); /* "</lock>" */ svn_xml_make_close_tag(&sb, pool, "lock"); } if (info->tree_conflict) SVN_ERR(svn_cl__append_tree_conflict_info_xml(sb, info->tree_conflict, pool)); /* "</entry>" */ svn_xml_make_close_tag(&sb, pool, "entry"); return svn_cl__error_checked_fputs(sb->data, stdout); }
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__info(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; apr_array_header_t *targets = NULL; apr_pool_t *subpool = svn_pool_create(pool); int i; svn_error_t *err; svn_boolean_t saw_a_problem = FALSE; svn_opt_revision_t peg_revision; svn_info_receiver_t receiver; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, ctx, pool)); /* Add "." if user passed 0 arguments. */ svn_opt_push_implicit_dot_target(targets, pool); if (opt_state->xml) { receiver = print_info_xml; /* If output is not incremental, output the XML header and wrap everything in a top-level element. This makes the output in its entirety a well-formed XML document. */ if (! opt_state->incremental) SVN_ERR(svn_cl__xml_print_header("info", pool)); } else { receiver = print_info; if (opt_state->incremental) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("'incremental' option only valid in XML " "mode")); } if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_empty; for (i = 0; i < targets->nelts; i++) { const char *truepath; const char *target = APR_ARRAY_IDX(targets, i, const char *); svn_pool_clear(subpool); SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); /* Get peg revisions. */ SVN_ERR(svn_opt_parse_path(&peg_revision, &truepath, target, subpool)); /* If no peg-rev was attached to a URL target, then assume HEAD. */ if ((svn_path_is_url(target)) && (peg_revision.kind == svn_opt_revision_unspecified)) peg_revision.kind = svn_opt_revision_head; err = svn_client_info2(truepath, &peg_revision, &(opt_state->start_revision), receiver, NULL, opt_state->depth, opt_state->changelists, ctx, subpool); if (err) { /* If one of the targets is a non-existent URL or wc-entry, don't bail out. Just warn and move on to the next target. */ if (err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE || err->apr_err == SVN_ERR_ENTRY_NOT_FOUND) { SVN_ERR(svn_cmdline_fprintf (stderr, subpool, _("%s: (Not a versioned resource)\n\n"), svn_path_local_style(target, pool))); } else if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) { SVN_ERR(svn_cmdline_fprintf (stderr, subpool, _("%s: (Not a valid URL)\n\n"), svn_path_local_style(target, pool))); } else { return err; } svn_error_clear(err); err = NULL; saw_a_problem = TRUE; } } svn_pool_destroy(subpool); if (opt_state->xml && (! opt_state->incremental)) SVN_ERR(svn_cl__xml_print_footer("info", pool)); if (saw_a_problem) return svn_error_create(SVN_ERR_BASE, NULL, NULL); else return SVN_NO_ERROR; }
/* A callback of type svn_info_receiver_t. */ static svn_error_t * print_info(void *baton, const char *target, const svn_info_t *info, apr_pool_t *pool) { SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"), svn_path_local_style(target, pool))); /* ### remove this someday: it's only here for cmdline output compatibility with svn 1.1 and older. */ if (info->kind != svn_node_dir) SVN_ERR(svn_cmdline_printf(pool, _("Name: %s\n"), svn_path_basename(target, pool))); if (info->URL) SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL)); if (info->repos_root_URL) SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"), info->repos_root_URL)); if (info->repos_UUID) SVN_ERR(svn_cmdline_printf(pool, _("Repository UUID: %s\n"), info->repos_UUID)); if (SVN_IS_VALID_REVNUM(info->rev)) SVN_ERR(svn_cmdline_printf(pool, _("Revision: %ld\n"), info->rev)); switch (info->kind) { case svn_node_file: SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: file\n"))); break; case svn_node_dir: SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: directory\n"))); break; case svn_node_none: SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: none\n"))); break; case svn_node_unknown: default: SVN_ERR(svn_cmdline_printf(pool, _("Node Kind: unknown\n"))); break; } if (info->has_wc_info) { switch (info->schedule) { case svn_wc_schedule_normal: SVN_ERR(svn_cmdline_printf(pool, _("Schedule: normal\n"))); break; case svn_wc_schedule_add: SVN_ERR(svn_cmdline_printf(pool, _("Schedule: add\n"))); break; case svn_wc_schedule_delete: SVN_ERR(svn_cmdline_printf(pool, _("Schedule: delete\n"))); break; case svn_wc_schedule_replace: SVN_ERR(svn_cmdline_printf(pool, _("Schedule: replace\n"))); break; default: break; } switch (info->depth) { case svn_depth_unknown: /* Unknown depth is the norm for remote directories anyway (although infinity would be equally appropriate). Let's not bother to print it. */ break; case svn_depth_empty: SVN_ERR(svn_cmdline_printf(pool, _("Depth: empty\n"))); break; case svn_depth_files: SVN_ERR(svn_cmdline_printf(pool, _("Depth: files\n"))); break; case svn_depth_immediates: SVN_ERR(svn_cmdline_printf(pool, _("Depth: immediates\n"))); break; case svn_depth_infinity: /* Infinity is the default depth for working copy directories. Let's not print it, it's not special enough to be worth mentioning. */ break; default: /* Other depths should never happen here. */ SVN_ERR(svn_cmdline_printf(pool, _("Depth: INVALID\n"))); } if (info->copyfrom_url) SVN_ERR(svn_cmdline_printf(pool, _("Copied From URL: %s\n"), info->copyfrom_url)); if (SVN_IS_VALID_REVNUM(info->copyfrom_rev)) SVN_ERR(svn_cmdline_printf(pool, _("Copied From Rev: %ld\n"), info->copyfrom_rev)); } if (info->last_changed_author) SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Author: %s\n"), info->last_changed_author)); if (SVN_IS_VALID_REVNUM(info->last_changed_rev)) SVN_ERR(svn_cmdline_printf(pool, _("Last Changed Rev: %ld\n"), info->last_changed_rev)); if (info->last_changed_date) SVN_ERR(svn_cl__info_print_time(info->last_changed_date, _("Last Changed Date"), pool)); if (info->has_wc_info) { if (info->text_time) SVN_ERR(svn_cl__info_print_time(info->text_time, _("Text Last Updated"), pool)); if (info->checksum) SVN_ERR(svn_cmdline_printf(pool, _("Checksum: %s\n"), info->checksum)); if (info->conflict_old) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Previous Base File: %s\n"), svn_path_local_style(info->conflict_old, pool))); if (info->conflict_wrk) SVN_ERR(svn_cmdline_printf (pool, _("Conflict Previous Working File: %s\n"), svn_path_local_style(info->conflict_wrk, pool))); if (info->conflict_new) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Current Base File: %s\n"), svn_path_local_style(info->conflict_new, pool))); if (info->prejfile) SVN_ERR(svn_cmdline_printf(pool, _("Conflict Properties File: %s\n"), svn_path_local_style(info->prejfile, pool))); } if (info->lock) { if (info->lock->token) SVN_ERR(svn_cmdline_printf(pool, _("Lock Token: %s\n"), info->lock->token)); if (info->lock->owner) SVN_ERR(svn_cmdline_printf(pool, _("Lock Owner: %s\n"), info->lock->owner)); if (info->lock->creation_date) SVN_ERR(svn_cl__info_print_time(info->lock->creation_date, _("Lock Created"), pool)); if (info->lock->expiration_date) SVN_ERR(svn_cl__info_print_time(info->lock->expiration_date, _("Lock Expires"), pool)); if (info->lock->comment) { int comment_lines; /* NOTE: The stdio will handle newline translation. */ comment_lines = svn_cstring_count_newlines(info->lock->comment) + 1; SVN_ERR(svn_cmdline_printf(pool, Q_("Lock Comment (%i line):\n%s\n", "Lock Comment (%i lines):\n%s\n", comment_lines), comment_lines, info->lock->comment)); } } if (info->changelist) SVN_ERR(svn_cmdline_printf(pool, _("Changelist: %s\n"), info->changelist)); if (info->tree_conflict) { const char *desc, *src_left_version, *src_right_version; SVN_ERR(svn_cl__get_human_readable_tree_conflict_description( &desc, info->tree_conflict, pool)); src_left_version = svn_cl__node_description(info->tree_conflict->src_left_version, pool); src_right_version = svn_cl__node_description(info->tree_conflict->src_right_version, pool); svn_cmdline_printf(pool, "%s: %s\n", _("Tree conflict"), desc); if (src_left_version) svn_cmdline_printf(pool, " %s: %s\n", _("Source left"), /* (1) */ src_left_version); /* (1): Sneaking in a space in "Source left" so that it is the * same length as "Source right" while it still starts in the same * column. That's just a tiny tweak in the English `svn'. */ if (src_right_version) svn_cmdline_printf(pool, " %s: %s\n", _("Source right"), src_right_version); } /* Print extra newline separator. */ return svn_cmdline_printf(pool, "\n"); }
svn_error_t * svn_client__switch_internal(svn_revnum_t *result_rev, const char *path, const char *switch_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_wc_adm_access_t *adm_access, svn_depth_t depth, svn_boolean_t depth_is_sticky, svn_boolean_t *timestamp_sleep, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_client_ctx_t *ctx, apr_pool_t *pool) { const svn_ra_reporter3_t *reporter; void *report_baton; const svn_wc_entry_t *entry; const char *URL, *anchor, *target, *source_root, *switch_rev_url; svn_ra_session_t *ra_session; svn_revnum_t revnum; svn_error_t *err = SVN_NO_ERROR; svn_wc_adm_access_t *dir_access; const svn_boolean_t close_adm_access = ! adm_access; const char *diff3_cmd; svn_boolean_t use_commit_times; svn_boolean_t sleep_here; svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here; const svn_delta_editor_t *switch_editor; void *switch_edit_baton; svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool); const char *preserved_exts_str; apr_array_header_t *preserved_exts; svn_boolean_t server_supports_depth; svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) depth_is_sticky = FALSE; /* Do not support the situation of both exclude and switch a target. */ if (depth_is_sticky && depth == svn_depth_exclude) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot both exclude and switch a path")); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) : NULL; /* Sanity check. Without these, the switch is meaningless. */ SVN_ERR_ASSERT(path); SVN_ERR_ASSERT(switch_url && (switch_url[0] != '\0')); /* ### Need to lock the whole target tree to invalidate wcprops. Does non-recursive switch really need to invalidate the whole tree? */ if (adm_access) { svn_wc_adm_access_t *a = adm_access; const char *dir_access_path; /* This is a little hacky, but open two new read-only access baton's to get the anchor and target access batons that would be used if a locked access baton was not available. */ SVN_ERR(svn_wc_adm_open_anchor(&adm_access, &dir_access, &target, path, FALSE, -1, ctx->cancel_func, ctx->cancel_baton, pool)); anchor = svn_wc_adm_access_path(adm_access); dir_access_path = svn_wc_adm_access_path(dir_access); SVN_ERR(svn_wc_adm_close2(adm_access, pool)); SVN_ERR(svn_wc_adm_retrieve(&adm_access, a, anchor, pool)); SVN_ERR(svn_wc_adm_retrieve(&dir_access, a, dir_access_path, pool)); } else { SVN_ERR(svn_wc_adm_open_anchor(&adm_access, &dir_access, &target, path, TRUE, -1, ctx->cancel_func, ctx->cancel_baton, pool)); anchor = svn_wc_adm_access_path(adm_access); } SVN_ERR(svn_wc__entry_versioned(&entry, anchor, adm_access, FALSE, pool)); if (! entry->url) return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, _("Directory '%s' has no URL"), svn_path_local_style(anchor, pool)); URL = apr_pstrdup(pool, entry->url); /* Open an RA session to 'source' URL */ SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, &switch_rev_url, switch_url, adm_access, peg_revision, revision, ctx, pool)); SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root, pool)); /* Disallow a switch operation to change the repository root of the target. */ if (! svn_path_is_ancestor(source_root, URL)) return svn_error_createf (SVN_ERR_WC_INVALID_SWITCH, NULL, _("'%s'\n" "is not the same repository as\n" "'%s'"), URL, source_root); /* We may need to crop the tree if the depth is sticky */ if (depth_is_sticky && depth < svn_depth_infinity) { const svn_wc_entry_t *target_entry; SVN_ERR(svn_wc_entry( &target_entry, svn_dirent_join(svn_wc_adm_access_path(adm_access), target, pool), adm_access, TRUE, pool)); if (target_entry && target_entry->kind == svn_node_dir) { SVN_ERR(svn_wc_crop_tree(adm_access, target, depth, ctx->notify_func2, ctx->notify_baton2, ctx->cancel_func, ctx->cancel_baton, pool)); } } SVN_ERR(svn_ra_reparent(ra_session, URL, pool)); /* Fetch the switch (update) editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision() later on. */ SVN_ERR(svn_wc_get_switch_editor3(&revnum, adm_access, target, switch_rev_url, use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, ctx->notify_func2, ctx->notify_baton2, ctx->cancel_func, ctx->cancel_baton, ctx->conflict_func, ctx->conflict_baton, diff3_cmd, preserved_exts, &switch_editor, &switch_edit_baton, traversal_info, pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum, target, depth, switch_rev_url, switch_editor, switch_edit_baton, pool)); SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, the update_editor will be driven by svn_repos_dir_delta2. We pass in a traversal_info for recording all externals. It shouldn't be needed for a switch if it wasn't for the relative externals of type '../path'. All of those must be resolved to the new location. */ err = svn_wc_crawl_revisions4(path, dir_access, reporter, report_baton, TRUE, depth, (! depth_is_sticky), (! server_supports_depth), use_commit_times, ctx->notify_func2, ctx->notify_baton2, traversal_info, pool); if (err) { /* Don't rely on the error handling to handle the sleep later, do it now */ svn_io_sleep_for_timestamps(path, pool); return err; } *use_sleep = TRUE; /* We handle externals after the switch is complete, so that handling external items (and any errors therefrom) doesn't delay the primary operation. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) err = svn_client__handle_externals(adm_access, traversal_info, switch_url, path, source_root, depth, use_sleep, ctx, pool); /* Sleep to ensure timestamp integrity (we do this regardless of errors in the actual switch operation(s)). */ if (sleep_here) svn_io_sleep_for_timestamps(path, pool); /* Return errors we might have sustained. */ if (err) return err; if (close_adm_access) SVN_ERR(svn_wc_adm_close2(adm_access, pool)); /* Let everyone know we're finished here. */ if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(anchor, svn_wc_notify_update_completed, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = revnum; return SVN_NO_ERROR; }
static void status_func (void *baton, const char *path, svn_wc_status2_t *status) { apr_pool_t *pool = ((status_bt *)baton)->pool; print_status (svn_path_local_style (path, pool), TRUE, TRUE, TRUE, status, ((status_bt *) baton)->L, pool); }
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__propdel(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 *pname, *pname_utf8; apr_array_header_t *args, *targets; int i; /* Get the property's name (and a UTF-8 version of that name). */ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool)); pname = APR_ARRAY_IDX(args, 0, const char *); SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool)); /* No need to check svn_prop_name_is_valid for *deleting* properties, and it may even be useful to allow, in case invalid properties sneaked through somehow. */ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, pool)); /* Add "." if user passed 0 file arguments */ svn_opt_push_implicit_dot_target(targets, pool); if (opt_state->revprop) /* operate on a revprop */ { svn_revnum_t rev; const char *URL; SVN_ERR(svn_cl__revprop_prepare(&opt_state->start_revision, targets, &URL, pool)); /* Let libsvn_client do the real work. */ SVN_ERR(svn_client_revprop_set(pname_utf8, NULL, URL, &(opt_state->start_revision), &rev, FALSE, ctx, pool)); if (! opt_state->quiet) { SVN_ERR(svn_cmdline_printf(pool, _("property '%s' deleted from" " repository revision %ld\n"), pname_utf8, rev)); } } else if (opt_state->start_revision.kind != svn_opt_revision_unspecified) { return svn_error_createf (SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("Cannot specify revision for deleting versioned property '%s'"), pname); } else /* operate on a normal, versioned property (not a revprop) */ { apr_pool_t *subpool = svn_pool_create(pool); if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_empty; /* For each target, remove the property PNAME. */ for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); svn_commit_info_t *commit_info; svn_boolean_t success; svn_pool_clear(subpool); SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); /* Pass FALSE for 'skip_checks' because it doesn't matter here, and opt_state->force doesn't apply to this command anyway. */ SVN_ERR(svn_cl__try(svn_client_propset3 (&commit_info, pname_utf8, NULL, target, opt_state->depth, FALSE, SVN_INVALID_REVNUM, opt_state->changelists, NULL, ctx, subpool), &success, opt_state->quiet, SVN_ERR_UNVERSIONED_RESOURCE, SVN_ERR_ENTRY_NOT_FOUND, SVN_NO_ERROR)); if (success && (! opt_state->quiet)) { SVN_ERR(svn_cmdline_printf (subpool, SVN_DEPTH_IS_RECURSIVE(opt_state->depth) ? _("property '%s' deleted (recursively) from '%s'.\n") : _("property '%s' deleted from '%s'.\n"), pname_utf8, svn_path_local_style(target, subpool))); } } svn_pool_destroy(subpool); } return SVN_NO_ERROR; }
svn_error_t * svn_cl__print_status_xml(const char *path, svn_wc_status2_t *status, apr_pool_t *pool) { svn_stringbuf_t *sb = svn_stringbuf_create("", pool); apr_hash_t *att_hash; if (status->text_status == svn_wc_status_none && status->repos_text_status == svn_wc_status_none) return SVN_NO_ERROR; svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", "path", svn_path_local_style(path, pool), NULL); att_hash = apr_hash_make(pool); apr_hash_set(att_hash, "item", APR_HASH_KEY_STRING, generate_status_desc(status->text_status)); apr_hash_set(att_hash, "props", APR_HASH_KEY_STRING, generate_status_desc(status->prop_status)); if (status->locked) apr_hash_set(att_hash, "wc-locked", APR_HASH_KEY_STRING, "true"); if (status->copied) apr_hash_set(att_hash, "copied", APR_HASH_KEY_STRING, "true"); if (status->switched) apr_hash_set(att_hash, "switched", APR_HASH_KEY_STRING, "true"); if (status->entry && ! status->entry->copied) apr_hash_set(att_hash, "revision", APR_HASH_KEY_STRING, apr_psprintf(pool, "%ld", status->entry->revision)); svn_xml_make_open_tag_hash(&sb, pool, svn_xml_normal, "wc-status", att_hash); if (status->entry && SVN_IS_VALID_REVNUM(status->entry->cmt_rev)) { svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "commit", "revision", apr_psprintf(pool, "%ld", status->entry->cmt_rev), NULL); svn_cl__xml_tagged_cdata(&sb, pool, "author", status->entry->cmt_author); if (status->entry->cmt_date) svn_cl__xml_tagged_cdata(&sb, pool, "date", svn_time_to_cstring (status->entry->cmt_date, pool)); svn_xml_make_close_tag(&sb, pool, "commit"); } if (status->entry && status->entry->lock_token) { svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "lock", NULL); svn_cl__xml_tagged_cdata(&sb, pool, "token", status->entry->lock_token); /* If lock_owner is NULL, assume WC is corrupt. */ if (status->entry->lock_owner) svn_cl__xml_tagged_cdata(&sb, pool, "owner", status->entry->lock_owner); else return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, _("'%s' has lock token, but no lock owner"), svn_path_local_style(path, pool)); svn_cl__xml_tagged_cdata(&sb, pool, "comment", status->entry->lock_comment); svn_cl__xml_tagged_cdata(&sb, pool, "created", svn_time_to_cstring (status->entry->lock_creation_date, pool)); svn_xml_make_close_tag(&sb, pool, "lock"); } svn_xml_make_close_tag(&sb, pool, "wc-status"); if (status->repos_text_status != svn_wc_status_none || status->repos_prop_status != svn_wc_status_none || status->repos_lock) { svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "repos-status", "item", generate_status_desc(status->repos_text_status), "props", generate_status_desc(status->repos_prop_status), NULL); if (status->repos_lock) { svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "lock", NULL); svn_cl__xml_tagged_cdata(&sb, pool, "token", status->repos_lock->token); svn_cl__xml_tagged_cdata(&sb, pool, "owner", status->repos_lock->owner); svn_cl__xml_tagged_cdata(&sb, pool, "comment", status->repos_lock->comment); svn_cl__xml_tagged_cdata(&sb, pool, "created", svn_time_to_cstring (status->repos_lock->creation_date, pool)); if (status->repos_lock->expiration_date != 0) { svn_cl__xml_tagged_cdata(&sb, pool, "expires", svn_time_to_cstring (status->repos_lock->expiration_date, pool)); } svn_xml_make_close_tag(&sb, pool, "lock"); } svn_xml_make_close_tag(&sb, pool, "repos-status"); } svn_xml_make_close_tag(&sb, pool, "entry"); return svn_cl__error_checked_fputs(sb->data, stdout); }
svn_error_t * svn_client_status4(svn_revnum_t *result_rev, const char *path, const svn_opt_revision_t *revision, svn_wc_status_func3_t status_func, void *status_baton, svn_depth_t depth, svn_boolean_t get_all, svn_boolean_t update, svn_boolean_t no_ignore, svn_boolean_t ignore_externals, const apr_array_header_t *changelists, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_wc_adm_access_t *anchor_access, *target_access; svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool); const char *anchor, *target; const svn_delta_editor_t *editor; void *edit_baton, *set_locks_baton; const svn_wc_entry_t *entry = NULL; struct status_baton sb; apr_array_header_t *ignores; svn_error_t *err; apr_hash_t *changelist_hash = NULL; svn_revnum_t edit_revision = SVN_INVALID_REVNUM; if (changelists && changelists->nelts) SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); sb.real_status_func = status_func; sb.real_status_baton = status_baton; sb.deleted_in_repos = FALSE; sb.changelist_hash = changelist_hash; /* Try to open the target directory. If the target is a file or an unversioned directory, open the parent directory instead */ err = svn_wc_adm_open3(&anchor_access, NULL, path, FALSE, SVN_DEPTH_IS_RECURSIVE(depth) ? -1 : 1, ctx->cancel_func, ctx->cancel_baton, pool); if (err && err->apr_err == SVN_ERR_WC_NOT_DIRECTORY) { svn_error_clear(err); SVN_ERR(svn_wc_adm_open_anchor(&anchor_access, &target_access, &target, path, FALSE, SVN_DEPTH_IS_RECURSIVE(depth) ? -1 : 1, ctx->cancel_func, ctx->cancel_baton, pool)); } else if (!err) { target = ""; target_access = anchor_access; } else return err; anchor = svn_wc_adm_access_path(anchor_access); /* Get the status edit, and use our wrapping status function/baton as the callback pair. */ SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool)); SVN_ERR(svn_wc_get_status_editor4(&editor, &edit_baton, &set_locks_baton, &edit_revision, anchor_access, target, depth, get_all, no_ignore, ignores, tweak_status, &sb, ctx->cancel_func, ctx->cancel_baton, traversal_info, pool)); /* If we want to know about out-of-dateness, we crawl the working copy and let the RA layer drive the editor for real. Otherwise, we just close the edit. :-) */ if (update) { svn_ra_session_t *ra_session; const char *URL; svn_node_kind_t kind; svn_boolean_t server_supports_depth; /* Get full URL from the ANCHOR. */ if (! entry) SVN_ERR(svn_wc__entry_versioned(&entry, anchor, anchor_access, FALSE, pool)); if (! entry->url) return svn_error_createf (SVN_ERR_ENTRY_MISSING_URL, NULL, _("Entry '%s' has no URL"), svn_path_local_style(anchor, pool)); URL = apr_pstrdup(pool, entry->url); /* Open a repository session to the URL. */ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL, anchor, anchor_access, NULL, FALSE, TRUE, ctx, pool)); /* Verify that URL exists in HEAD. If it doesn't, this can save us a whole lot of hassle; if it does, the cost of this request should be minimal compared to the size of getting back the average amount of "out-of-date" information. */ SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind, pool)); if (kind == svn_node_none) { /* Our status target does not exist in HEAD of the repository. If we're just adding this thing, that's fine. But if it was previously versioned, then it must have been deleted from the repository. */ if (entry->schedule != svn_wc_schedule_add) sb.deleted_in_repos = TRUE; /* And now close the edit. */ SVN_ERR(editor->close_edit(edit_baton, pool)); } else { svn_revnum_t revnum; report_baton_t rb; if (revision->kind == svn_opt_revision_head) { /* Cause the revision number to be omitted from the request, which implies HEAD. */ revnum = SVN_INVALID_REVNUM; } else { /* Get a revision number for our status operation. */ SVN_ERR(svn_client__get_revision_number (&revnum, NULL, ra_session, revision, target, pool)); } /* Do the deed. Let the RA layer drive the status editor. */ SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter, &rb.wrapped_report_baton, target, revnum, depth, editor, edit_baton, pool)); /* Init the report baton. */ rb.ancestor = apr_pstrdup(pool, URL); rb.set_locks_baton = set_locks_baton; rb.ctx = ctx; rb.pool = pool; SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, EDITOR will be driven to describe differences between our working copy and HEAD. */ SVN_ERR(svn_wc_crawl_revisions4(path, target_access, &lock_fetch_reporter, &rb, FALSE, depth, TRUE, (! server_supports_depth), FALSE, NULL, NULL, NULL, pool)); } } else { SVN_ERR(editor->close_edit(edit_baton, pool)); } if (ctx->notify_func2 && update) { svn_wc_notify_t *notify = svn_wc_create_notify(path, svn_wc_notify_status_completed, pool); notify->revision = edit_revision; (ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = edit_revision; /* Close the access baton here, as svn_client__do_external_status() calls back into this function and thus will be re-opening the working copy. */ SVN_ERR(svn_wc_adm_close2(anchor_access, pool)); /* If there are svn:externals set, we don't want those to show up as unversioned or unrecognized, so patch up the hash. If caller wants all the statuses, we will change unversioned status items that are interesting to an svn:externals property to svn_wc_status_unversioned, otherwise we'll just remove the status item altogether. We only descend into an external if depth is svn_depth_infinity or svn_depth_unknown. However, there are conceivable behaviors that would involve descending under other circumstances; thus, we pass depth anyway, so the code will DTRT if we change the conditional in the future. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) SVN_ERR(svn_client__do_external_status(traversal_info, status_func, status_baton, depth, get_all, update, no_ignore, ctx, pool)); return SVN_NO_ERROR; }
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__status(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; apr_array_header_t *targets; apr_pool_t *subpool; apr_hash_t *master_cl_hash = apr_hash_make(pool); int i; svn_opt_revision_t rev; struct status_baton sb; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, ctx, pool)); /* Add "." if user passed 0 arguments */ svn_opt_push_implicit_dot_target(targets, pool); /* We want our -u statuses to be against HEAD. */ rev.kind = svn_opt_revision_head; /* The notification callback, leave the notifier as NULL in XML mode */ if (! opt_state->xml) svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE, FALSE, FALSE, pool); subpool = svn_pool_create(pool); sb.had_print_error = FALSE; if (opt_state->xml) { /* If output is not incremental, output the XML header and wrap everything in a top-level element. This makes the output in its entirety a well-formed XML document. */ if (! opt_state->incremental) SVN_ERR(svn_cl__xml_print_header("status", pool)); } else { if (opt_state->incremental) return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, _("'incremental' option only valid in XML " "mode")); } sb.detailed = (opt_state->verbose || opt_state->update); sb.show_last_committed = opt_state->verbose; sb.skip_unrecognized = opt_state->quiet; sb.repos_locks = opt_state->update; sb.xml_mode = opt_state->xml; sb.cached_changelists = master_cl_hash; sb.cl_pool = pool; SVN_ERR(svn_opt__eat_peg_revisions(&targets, targets, pool)); for (i = 0; i < targets->nelts; i++) { const char *target = APR_ARRAY_IDX(targets, i, const char *); svn_revnum_t repos_rev = SVN_INVALID_REVNUM; svn_pool_clear(subpool); SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton)); if (opt_state->xml) SVN_ERR(print_start_target_xml(svn_path_local_style(target, subpool), subpool)); /* Retrieve a hash of status structures with the information requested by the user. */ SVN_ERR(svn_cl__try(svn_client_status4(&repos_rev, target, &rev, print_status, &sb, opt_state->depth, opt_state->verbose, opt_state->update, opt_state->no_ignore, opt_state->ignore_externals, opt_state->changelists, ctx, subpool), NULL, opt_state->quiet, /* not versioned: */ SVN_ERR_WC_NOT_DIRECTORY, SVN_NO_ERROR)); if (opt_state->xml) SVN_ERR(print_finish_target_xml(repos_rev, subpool)); } /* If any paths were cached because they were associatied with changelists, we can now display them as grouped changelists. */ if (apr_hash_count(master_cl_hash) > 0) { apr_hash_index_t *hi; svn_stringbuf_t *buf; if (opt_state->xml) buf = svn_stringbuf_create("", pool); for (hi = apr_hash_first(pool, master_cl_hash); hi; hi = apr_hash_next(hi)) { const char *changelist_name; apr_array_header_t *path_array; const void *key; void *val; int j; apr_hash_this(hi, &key, NULL, &val); changelist_name = key; path_array = val; /* ### TODO: For non-XML output, we shouldn't print the ### leading \n on the first changelist if there were no ### non-changelist entries. */ if (opt_state->xml) { svn_stringbuf_set(buf, ""); svn_xml_make_open_tag(&buf, pool, svn_xml_normal, "changelist", "name", changelist_name, NULL); SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout)); } else SVN_ERR(svn_cmdline_printf(pool, _("\n--- Changelist '%s':\n"), changelist_name)); for (j = 0; j < path_array->nelts; j++) { struct status_cache *scache = APR_ARRAY_IDX(path_array, j, struct status_cache *); SVN_ERR(print_status_normal_or_xml(&sb, scache->path, scache->status, pool)); } if (opt_state->xml) { svn_stringbuf_set(buf, ""); svn_xml_make_close_tag(&buf, pool, "changelist"); SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout)); } } }
svn_error_t * svn_sqlite__open(svn_sqlite__db_t **db, const char *path, svn_sqlite__mode_t mode, const char * const statements[], int latest_schema, const char * const *upgrade_sql, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { SVN_ERR(svn_atomic__init_once(&sqlite_init_state, init_sqlite, NULL, scratch_pool)); *db = apr_palloc(result_pool, sizeof(**db)); #if SQLITE_VERSION_AT_LEAST(3,5,0) { int flags; if (mode == svn_sqlite__mode_readonly) flags = SQLITE_OPEN_READONLY; else if (mode == svn_sqlite__mode_readwrite) flags = SQLITE_OPEN_READWRITE; else if (mode == svn_sqlite__mode_rwcreate) flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; else SVN_ERR_MALFUNCTION(); /* If this flag is defined (3.6.x), then let's turn off SQLite's mutexes. All svn objects are single-threaded, so we can already guarantee that our use of the SQLite handle will be serialized properly. Note: in 3.6.x, we've already config'd SQLite into MULTITHREAD mode, so this is probably redundant... */ /* ### yeah. remove when autoconf magic is done for init/config. */ #ifdef SQLITE_OPEN_NOMUTEX flags |= SQLITE_OPEN_NOMUTEX; #endif /* Open the database. Note that a handle is returned, even when an error occurs (except for out-of-memory); thus, we can safely use it to extract an error message and construct an svn_error_t. */ SQLITE_ERR(sqlite3_open_v2(path, &(*db)->db3, flags, NULL), *db); } #else /* Older versions of SQLite (pre-3.5.x) will always create the database if it doesn't exist. So, if we are asked to be read-only or read-write, we ensure the database already exists - if it doesn't, then we will explicitly error out before asking SQLite to do anything. Pre-3.5.x SQLite versions also don't support read-only ops either. */ if (mode == svn_sqlite__mode_readonly || mode == svn_sqlite__mode_readwrite) { svn_node_kind_t kind; SVN_ERR(svn_io_check_path(path, &kind, scratch_pool)); if (kind != svn_node_file) { return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, _("Expected SQLite database not found: %s"), svn_path_local_style(path, scratch_pool)); } } else if (mode == svn_sqlite__mode_rwcreate) { /* do nothing - older SQLite's will create automatically. */ } else SVN_ERR_MALFUNCTION(); SQLITE_ERR(sqlite3_open(path, &(*db)->db3), *db); #endif /* Retry until timeout when database is busy. */ SQLITE_ERR(sqlite3_busy_timeout((*db)->db3, BUSY_TIMEOUT), *db); #ifdef SQLITE3_DEBUG sqlite3_trace((*db)->db3, sqlite_tracer, (*db)->db3); #endif SVN_ERR(svn_sqlite__exec(*db, "PRAGMA case_sensitive_like=on;")); /* Validate the schema, upgrading if necessary. */ SVN_ERR(check_format(*db, latest_schema, upgrade_sql, scratch_pool)); /* Store the provided statements. */ if (statements) { (*db)->statement_strings = statements; (*db)->nbr_statements = 0; while (*statements != NULL) { statements++; (*db)->nbr_statements++; } (*db)->prepared_stmts = apr_pcalloc(result_pool, (*db)->nbr_statements * sizeof(svn_sqlite__stmt_t *)); } else (*db)->nbr_statements = 0; (*db)->result_pool = result_pool; apr_pool_cleanup_register(result_pool, *db, close_apr, apr_pool_cleanup_null); return SVN_NO_ERROR; }