/* Implements svn_ra_neon__startelm_cb_t. */ static svn_error_t * start_207_element(int *elem, void *baton, int parent, const char *nspace, const char *name, const char **atts) { multistatus_baton_t *b = baton; const svn_ra_neon__xml_elm_t *elm = svn_ra_neon__lookup_xml_elem(multistatus_elements, nspace, name); *elem = elm ? validate_element(parent, elm->id) : SVN_RA_NEON__XML_DECLINE; if (parent == ELEM_prop) { svn_stringbuf_setempty(b->propname); if (strcmp(nspace, SVN_DAV_PROP_NS_SVN) == 0) svn_stringbuf_set(b->propname, SVN_PROP_PREFIX); else if (strcmp(nspace, "DAV:") == 0) svn_stringbuf_set(b->propname, "DAV:"); svn_stringbuf_appendcstr(b->propname, name); } if (*elem < 1) /* ! > 0 */ return SVN_NO_ERROR; switch (*elem) { case ELEM_propstat: b->in_propstat = TRUE; b->propstat_has_error = FALSE; break; default: break; } /* We're guaranteed to have ELM now: SVN_RA_NEON__XML_DECLINE < 1 */ if (elm->flags & SVN_RA_NEON__XML_CDATA) { svn_stringbuf_setempty(b->cdata); b->want_cdata = b->cdata; } return SVN_NO_ERROR; }
/* Return a pointer to an option in CFG, or NULL if it doesn't exist. if SECTIONP is non-null, return a pointer to the option's section. OPTION may be NULL. */ static cfg_option_t * find_option(svn_config_t *cfg, const char *section, const char *option, cfg_section_t **sectionp) { void *sec_ptr; /* Canonicalize the hash key */ svn_stringbuf_set(cfg->tmp_key, section); make_hash_key(cfg->tmp_key->data); sec_ptr = apr_hash_get(cfg->sections, cfg->tmp_key->data, cfg->tmp_key->len); if (sectionp != NULL) *sectionp = sec_ptr; if (sec_ptr != NULL && option != NULL) { cfg_section_t *sec = sec_ptr; cfg_option_t *opt; /* Canonicalize the option key */ svn_stringbuf_set(cfg->tmp_key, option); make_hash_key(cfg->tmp_key->data); opt = apr_hash_get(sec->options, cfg->tmp_key->data, cfg->tmp_key->len); /* NOTE: ConfigParser's sections are case sensitive. */ if (opt == NULL && apr_strnatcasecmp(section, SVN_CONFIG__DEFAULT_SECTION) != 0) /* Options which aren't found in the requested section are also sought after in the default section. */ opt = find_option(cfg, SVN_CONFIG__DEFAULT_SECTION, option, &sec); return opt; } return NULL; }
/* construct the repos-local name for the given DAV property name */ static void get_repos_propname(dav_db *db, const dav_prop_name *name, const char **repos_propname) { if (strcmp(name->ns, SVN_DAV_PROP_NS_SVN) == 0) { /* recombine the namespace ("svn:") and the name. */ svn_stringbuf_set(db->work, SVN_PROP_PREFIX); svn_stringbuf_appendcstr(db->work, name->name); *repos_propname = db->work->data; } else if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0) { /* the name of a custom prop is just the name -- no ns URI */ *repos_propname = name->name; } else { *repos_propname = NULL; } }
/* A helper function to parse_local_abspath() which returns the on-disk KIND of LOCAL_ABSPATH, using DB and SCRATCH_POOL as needed. This function may do strange things, but at long as it comes up with the Right Answer, we should be happy. */ static svn_error_t * get_path_kind(svn_node_kind_t *kind, svn_wc__db_t *db, const char *local_abspath, apr_pool_t *scratch_pool) { svn_boolean_t special; svn_node_kind_t node_kind; /* This implements a *really* simple LRU cache, where "simple" is defined as "only one element". In other words, we remember the most recently queried path, and nothing else. This gives >80% cache hits. */ if (db->parse_cache.abspath && strcmp(db->parse_cache.abspath->data, local_abspath) == 0) { /* Cache hit! */ *kind = db->parse_cache.kind; return SVN_NO_ERROR; } if (!db->parse_cache.abspath) { db->parse_cache.abspath = svn_stringbuf_create(local_abspath, db->state_pool); } else { svn_stringbuf_set(db->parse_cache.abspath, local_abspath); } SVN_ERR(svn_io_check_special_path(local_abspath, &node_kind, &special, scratch_pool)); db->parse_cache.kind = (special ? svn_node_symlink : node_kind); *kind = db->parse_cache.kind; 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)); } } }
/* Implements svn_ra_neon__endelm_cb_t . */ static svn_error_t * end_207_element(void *baton, int state, const char *nspace, const char *name) { multistatus_baton_t *b = baton; switch (state) { case ELEM_multistatus: if (b->contains_error) { if (svn_stringbuf_isempty(b->description)) return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, _("The request response contained at least " "one error")); else if (b->contains_precondition_error) return svn_error_create(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL, b->description->data); else return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, b->description->data); } break; case ELEM_responsedescription: if (b->in_propstat) svn_stringbuf_set(b->propstat_description, b->cdata->data); else { if (! svn_stringbuf_isempty(b->description)) svn_stringbuf_appendcstr(b->description, "\n"); svn_stringbuf_appendstr(b->description, b->cdata); } break; case ELEM_status: { ne_status status; if (ne_parse_statusline(b->cdata->data, &status) == 0) { /*### I wanted ||=, but I guess the end result is the same */ if (! b->in_propstat) b->contains_error |= (status.klass != 2); else b->propstat_has_error = (status.klass != 2); /* Handle "412 Precondition Failed" specially */ if (status.code == 412) b->contains_precondition_error = TRUE; free(status.reason_phrase); } else return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, _("The response contains a non-conforming " "HTTP status line")); } break; case ELEM_propstat: b->in_propstat = FALSE; b->contains_error |= b->propstat_has_error; svn_stringbuf_appendcstr(b->description, apr_psprintf(b->req->pool, _("Error setting property '%s': "), b->propname->data)); svn_stringbuf_appendstr(b->description, b->propstat_description); default: /* do nothing */ break; } /* When we have an element which wants cdata, we'll set it all up in start_207_element() again */ b->want_cdata = NULL; return SVN_NO_ERROR; }
/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * item_closed(svn_ra_serf__xml_estate_t *xes, void *baton, int leaving_state, const svn_string_t *cdata, apr_hash_t *attrs, apr_pool_t *scratch_pool) { list_context_t *list_ctx = baton; if (leaving_state == AUTHOR) { /* For compatibility with liveprops, current servers will not use * base64-encoding for "binary" user names bu simply drop the * offending control chars. * * We might want to switch to revprop-style encoding, though, * and this is the code to do that. */ const char *encoding = svn_hash_gets(attrs, "encoding"); if (encoding) { /* Check for a known encoding type. This is easy -- there's only one. */ if (strcmp(encoding, "base64") != 0) { return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Unsupported encoding '%s'"), encoding); } cdata = svn_base64_decode_string(cdata, scratch_pool); } /* Remember until the next ITEM closing tag. */ svn_stringbuf_set(list_ctx->author_buf, cdata->data); list_ctx->author = list_ctx->author_buf->data; } else if (leaving_state == ITEM) { const char *dirent_path = cdata->data; const char *kind_word, *date, *crev, *size; svn_dirent_t dirent = { 0 }; kind_word = svn_hash_gets(attrs, "node-kind"); size = svn_hash_gets(attrs, "size"); dirent.has_props = svn_hash__get_bool(attrs, "has-props", FALSE); crev = svn_hash_gets(attrs, "created-rev"); date = svn_hash_gets(attrs, "date"); /* Convert data. */ dirent.kind = svn_node_kind_from_word(kind_word); if (size) SVN_ERR(svn_cstring_atoi64(&dirent.size, size)); else dirent.size = SVN_INVALID_FILESIZE; if (crev) SVN_ERR(svn_revnum_parse(&dirent.created_rev, crev, NULL)); else dirent.created_rev = SVN_INVALID_REVNUM; if (date) SVN_ERR(svn_time_from_cstring(&dirent.time, date, scratch_pool)); if (list_ctx->author) dirent.last_author = list_ctx->author; /* Invoke RECEIVER */ SVN_ERR(list_ctx->receiver(dirent_path, &dirent, list_ctx->receiver_baton, scratch_pool)); /* Reset buffered info. */ list_ctx->author = NULL; } return SVN_NO_ERROR; }
svn_error_t * svn_ra_neon__search_for_starting_props(svn_ra_neon__resource_t **rsrc, const char **missing_path, svn_ra_neon__session_t *sess, const char *url, apr_pool_t *pool) { svn_error_t *err = SVN_NO_ERROR; apr_size_t len; svn_stringbuf_t *path_s; ne_uri parsed_url; svn_stringbuf_t *lopped_path = svn_stringbuf_create(url, pool); /* initialize to make sure it'll fit without reallocating */ apr_pool_t *iterpool = svn_pool_create(pool); /* Split the url into its component pieces (scheme, host, path, etc). We want the path part. */ ne_uri_parse(url, &parsed_url); if (parsed_url.path == NULL) { ne_uri_free(&parsed_url); return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Neon was unable to parse URL '%s'"), url); } svn_stringbuf_setempty(lopped_path); path_s = svn_stringbuf_create(parsed_url.path, pool); ne_uri_free(&parsed_url); /* Try to get the starting_props from the public url. If the resource no longer exists in HEAD, we'll get a failure. That's fine: just keep removing components and trying to get the starting_props from parent directories. */ while (! svn_path_is_empty(path_s->data)) { svn_pool_clear(iterpool); err = svn_ra_neon__get_starting_props(rsrc, sess, path_s->data, NULL, iterpool); if (! err) break; /* found an existing parent! */ if (err->apr_err != SVN_ERR_FS_NOT_FOUND) return err; /* found a _real_ error */ /* else... lop off the basename and try again. */ svn_stringbuf_set(lopped_path, svn_path_join(svn_path_basename(path_s->data, iterpool), lopped_path->data, iterpool)); len = path_s->len; svn_path_remove_component(path_s); /* if we detect an infinite loop, get out. */ if (path_s->len == len) return svn_error_quick_wrap (err, _("The path was not part of a repository")); svn_error_clear(err); } /* error out if entire URL was bogus (not a single part of it exists in the repository!) */ if (svn_path_is_empty(path_s->data)) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("No part of path '%s' was found in " "repository HEAD"), parsed_url.path); /* Duplicate rsrc out of iterpool into pool */ { apr_hash_index_t *hi; svn_ra_neon__resource_t *tmp = apr_pcalloc(pool, sizeof(*tmp)); tmp->url = apr_pstrdup(pool, (*rsrc)->url); tmp->is_collection = (*rsrc)->is_collection; tmp->pool = pool; tmp->propset = apr_hash_make(pool); for (hi = apr_hash_first(iterpool, (*rsrc)->propset); hi; hi = apr_hash_next(hi)) { const void *key; void *val; apr_hash_this(hi, &key, NULL, &val); apr_hash_set(tmp->propset, apr_pstrdup(pool, key), APR_HASH_KEY_STRING, svn_string_dup(val, pool)); } *rsrc = tmp; } *missing_path = lopped_path->data; svn_pool_destroy(iterpool); return SVN_NO_ERROR; }