/* POOL may be NULL if the lifetime of LOCAL_ABSPATH is sufficient. */ static const char * compute_relpath(const svn_wc__db_wcroot_t *wcroot, const char *local_abspath, apr_pool_t *result_pool) { const char *relpath = svn_dirent_is_child(wcroot->abspath, local_abspath, result_pool); if (relpath == NULL) return ""; return relpath; }
/* Remove the conflict markers of NODE_ABSPATH, that were left over after copying NODE_ABSPATH from SRC_ABSPATH. Only use this function when you know what you're doing. This function explicitly ignores some case insensitivity issues! */ static svn_error_t * remove_node_conflict_markers(svn_wc__db_t *db, const char *src_abspath, const char *node_abspath, apr_pool_t *scratch_pool) { svn_skel_t *conflict; SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath, scratch_pool, scratch_pool)); /* Do we have conflict markers that should be removed? */ if (conflict != NULL) { const apr_array_header_t *markers; int i; const char *src_dir = svn_dirent_dirname(src_abspath, scratch_pool); const char *dst_dir = svn_dirent_dirname(node_abspath, scratch_pool); SVN_ERR(svn_wc__conflict_read_markers(&markers, db, src_abspath, conflict, scratch_pool, scratch_pool)); /* No iterpool: Maximum number of possible conflict markers is 4 */ for (i = 0; markers && (i < markers->nelts); i++) { const char *marker_abspath; const char *child_relpath; const char *child_abpath; marker_abspath = APR_ARRAY_IDX(markers, i, const char *); child_relpath = svn_dirent_is_child(src_dir, marker_abspath, NULL); if (child_relpath) { child_abpath = svn_dirent_join(dst_dir, child_relpath, scratch_pool); SVN_ERR(svn_io_remove_file2(child_abpath, TRUE, scratch_pool)); } } } return SVN_NO_ERROR; }
/* The main logic of the public svn_client_add4. * * EXISTING_PARENT_ABSPATH is the absolute path to the first existing * parent directory of local_abspath. If not NULL, all missing parents * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */ static svn_error_t * add(const char *local_abspath, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, const char *existing_parent_abspath, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { svn_node_kind_t kind; svn_error_t *err; svn_magic__cookie_t *magic_cookie; svn_magic__init(&magic_cookie, scratch_pool); if (existing_parent_abspath) { const char *parent_abspath; const char *child_relpath; apr_array_header_t *components; int i; apr_pool_t *iterpool; parent_abspath = existing_parent_abspath; child_relpath = svn_dirent_is_child(existing_parent_abspath, local_abspath, NULL); components = svn_path_decompose(child_relpath, scratch_pool); iterpool = svn_pool_create(scratch_pool); for (i = 0; i < components->nelts - 1; i++) { const char *component; svn_node_kind_t disk_kind; svn_pool_clear(iterpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); component = APR_ARRAY_IDX(components, i, const char *); parent_abspath = svn_dirent_join(parent_abspath, component, scratch_pool); SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool)); if (disk_kind != svn_node_none && disk_kind != svn_node_dir) return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, _("'%s' prevents creating parent of '%s'"), parent_abspath, local_abspath); SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool)); SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, parent_abspath, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); } svn_pool_destroy(iterpool); } SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); if (kind == svn_node_dir) { /* We use add_dir_recursive for all directory targets and pass depth along no matter what it is, so that the target's depth will be set correctly. */ err = add_dir_recursive(local_abspath, depth, force, no_ignore, magic_cookie, ctx, scratch_pool); } else if (kind == svn_node_file) err = add_file(local_abspath, magic_cookie, ctx, scratch_pool); else if (kind == svn_node_none) { svn_boolean_t tree_conflicted; /* Provide a meaningful error message if the node does not exist * on disk but is a tree conflict victim. */ err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, ctx->wc_ctx, local_abspath, scratch_pool); if (err) svn_error_clear(err); else if (tree_conflicted) return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, _("'%s' is an existing item in conflict; " "please mark the conflict as resolved " "before adding a new item here"), svn_dirent_local_style(local_abspath, scratch_pool)); return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, _("'%s' not found"), svn_dirent_local_style(local_abspath, scratch_pool)); } else return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Unsupported node kind for path '%s'"), svn_dirent_local_style(local_abspath, scratch_pool)); /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */ if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) { svn_error_clear(err); err = SVN_NO_ERROR; } return svn_error_trace(err); }
svn_error_t * svn_cl__get_log_message(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool) { svn_stringbuf_t *default_msg = NULL; struct log_msg_baton *lmb = baton; svn_stringbuf_t *message = NULL; svn_config_t *cfg; const char *mfc_after, *sponsored_by; cfg = lmb->config ? svn_hash_gets(lmb->config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; /* Set default message. */ default_msg = svn_stringbuf_create(APR_EOL_STR, pool); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "PR:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Submitted by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reported by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Reviewed by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Approved by:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Obtained from:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "MFC after:\t"); svn_config_get(cfg, &mfc_after, SVN_CONFIG_SECTION_MISCELLANY, "freebsd-mfc-after", NULL); if (mfc_after != NULL) svn_stringbuf_appendcstr(default_msg, mfc_after); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "MFH:\t\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Relnotes:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Security:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Sponsored by:\t"); svn_config_get(cfg, &sponsored_by, SVN_CONFIG_SECTION_MISCELLANY, "freebsd-sponsored-by", #ifdef HAS_ORGANIZATION_NAME ORGANIZATION_NAME); #else NULL); #endif if (sponsored_by != NULL) svn_stringbuf_appendcstr(default_msg, sponsored_by); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "Differential Revision:\t" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Description of fields to fill in above: 76 columns --|" APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> PR: If and which Problem Report is related." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Submitted by: If someone else sent in the change." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Reported by: If someone else reported the issue." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Reviewed by: If someone else reviewed your modification." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Approved by: If you needed approval for this commit." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Obtained from: If the change is from a third party." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> MFC after: N [day[s]|week[s]|month[s]]. Request a reminder email." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> MFH: Ports tree branch name. Request approval for merge." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Relnotes: Set to 'yes' for mention in release notes." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Security: Vulnerability reference (one per line) or description." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Sponsored by: If the change was sponsored by an organization." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Differential Revision: https://reviews.freebsd.org/D### (*full* phabric URL needed)." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, "> Empty fields above will be automatically removed." APR_EOL_STR); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR); *tmp_file = NULL; if (lmb->message) { svn_string_t *log_msg_str = svn_string_create(lmb->message, pool); SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, NULL, NULL, log_msg_str, lmb->message_encoding, FALSE, pool, pool), _("Error normalizing log message to internal format")); /* Strip off the EOF marker text and the junk that follows it. */ truncate_buffer_at_prefix(&(log_msg_str->len), (char *)log_msg_str->data, EDITOR_EOF_PREFIX); cleanmsg(&(log_msg_str->len), (char*)log_msg_str->data); *log_msg = log_msg_str->data; return SVN_NO_ERROR; } if (! commit_items->nelts) { *log_msg = ""; return SVN_NO_ERROR; } while (! message) { /* We still don't have a valid commit message. Use $EDITOR to get one. Note that svn_cl__edit_string_externally will still return a UTF-8'ized log message. */ int i; svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool); svn_error_t *err = SVN_NO_ERROR; svn_string_t *msg_string = svn_string_create_empty(pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *); const char *path = item->path; char text_mod = '_', prop_mod = ' ', unlock = ' '; if (! path) path = item->url; else if (lmb->base_dir) path = svn_dirent_is_child(lmb->base_dir, path, pool); /* If still no path, then just use current directory. */ if (! path || !*path) path = "."; if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) text_mod = 'R'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) text_mod = 'A'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) text_mod = 'D'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) text_mod = 'M'; if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) prop_mod = 'M'; if (! lmb->keep_locks && item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN) unlock = 'U'; svn_stringbuf_appendbyte(tmp_message, text_mod); svn_stringbuf_appendbyte(tmp_message, prop_mod); svn_stringbuf_appendbyte(tmp_message, unlock); if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) /* History included via copy/move. */ svn_stringbuf_appendcstr(tmp_message, "+ "); else svn_stringbuf_appendcstr(tmp_message, " "); svn_stringbuf_appendcstr(tmp_message, path); svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR); } msg_string->data = tmp_message->data; msg_string->len = tmp_message->len; /* Use the external edit to get a log message. */ if (! lmb->non_interactive) { err = svn_cmdline__edit_string_externally(&msg_string, &lmb->tmpfile_left, lmb->editor_cmd, lmb->base_dir ? lmb->base_dir : "", msg_string, "svn-commit", lmb->config, TRUE, lmb->message_encoding, pool); } else /* non_interactive flag says we can't pop up an editor, so error */ { return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("Cannot invoke editor to get log message " "when non-interactive")); } /* Dup the tmpfile path into its baton's pool. */ *tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool, lmb->tmpfile_left); /* If the edit returned an error, handle it. */ if (err) { if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR) err = svn_error_quick_wrap (err, _("Could not use external editor to fetch log message; " "consider setting the $SVN_EDITOR environment variable " "or using the --message (-m) or --file (-F) options")); return svn_error_trace(err); } if (msg_string) message = svn_stringbuf_create_from_string(msg_string, pool); /* Strip off the EOF marker text and the junk that follows it. */ if (message) truncate_buffer_at_prefix(&message->len, message->data, EDITOR_EOF_PREFIX); /* * Since we're adding freebsd-specific tokens to the log message, * clean out any leftovers to avoid accidently sending them to other * projects that won't be expecting them. */ if (message) cleanmsg(&message->len, message->data); if (message) { /* We did get message, now check if it is anything more than just white space as we will consider white space only as empty */ apr_size_t len; for (len = 0; len < message->len; len++) { /* FIXME: should really use an UTF-8 whitespace test rather than svn_ctype_isspace, which is ASCII only */ if (! svn_ctype_isspace(message->data[len])) break; } if (len == message->len) message = NULL; } if (! message) { const char *reply; SVN_ERR(svn_cmdline_prompt_user2 (&reply, _("\nLog message unchanged or not specified\n" "(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool)); if (reply) { int letter = apr_tolower(reply[0]); /* If the user chooses to abort, we cleanup the temporary file and exit the loop with a NULL message. */ if ('a' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; break; } /* If the user chooses to continue, we make an empty message, which will cause us to exit the loop. We also cleanup the temporary file. */ if ('c' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; message = svn_stringbuf_create_empty(pool); } /* If the user chooses anything else, the loop will continue on the NULL message. */ } } } *log_msg = message ? message->data : NULL; return SVN_NO_ERROR; }
/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself, but create it marked with skip+skip_children. */ static svn_error_t * ensure_state(struct diff_baton *eb, const char *local_abspath, svn_boolean_t recursive_skip, apr_pool_t *scratch_pool) { struct node_state_t *ns; apr_pool_t *ns_pool; if (!eb->cur) { if (!svn_dirent_is_ancestor(eb->anchor_abspath, local_abspath)) return SVN_NO_ERROR; SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), FALSE, scratch_pool)); } else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL)) SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), FALSE, scratch_pool)); else return SVN_NO_ERROR; if (eb->cur && eb->cur->skip_children) return SVN_NO_ERROR; ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool); ns = apr_pcalloc(ns_pool, sizeof(*ns)); ns->pool = ns_pool; ns->local_abspath = apr_pstrdup(ns_pool, local_abspath); ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath); ns->parent = eb->cur; eb->cur = ns; if (recursive_skip) { ns->skip = TRUE; ns->skip_children = TRUE; return SVN_NO_ERROR; } { svn_revnum_t revision; svn_error_t *err; err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, eb->db, local_abspath, scratch_pool, scratch_pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); svn_error_clear(err); revision = 0; /* Use original revision? */ } ns->left_src = svn_diff__source_create(revision, ns->pool); ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool); SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip, &ns->skip_children, ns->relpath, ns->left_src, ns->right_src, NULL /* copyfrom_source */, ns->parent ? ns->parent->baton : NULL, eb->processor, ns->pool, scratch_pool)); } return SVN_NO_ERROR; }
svn_error_t * svn_cl__get_log_message(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool) { svn_stringbuf_t *default_msg = NULL; struct log_msg_baton *lmb = baton; svn_stringbuf_t *message = NULL; /* Set default message. */ default_msg = svn_stringbuf_create(APR_EOL_STR, pool); svn_stringbuf_appendcstr(default_msg, EDITOR_EOF_PREFIX); svn_stringbuf_appendcstr(default_msg, APR_EOL_STR APR_EOL_STR); *tmp_file = NULL; if (lmb->message) { svn_stringbuf_t *log_msg_buf = svn_stringbuf_create(lmb->message, pool); svn_string_t *log_msg_str = apr_pcalloc(pool, sizeof(*log_msg_str)); /* Trim incoming messages of the EOF marker text and the junk that follows it. */ truncate_buffer_at_prefix(&(log_msg_buf->len), log_msg_buf->data, EDITOR_EOF_PREFIX); /* Make a string from a stringbuf, sharing the data allocation. */ log_msg_str->data = log_msg_buf->data; log_msg_str->len = log_msg_buf->len; SVN_ERR_W(svn_subst_translate_string2(&log_msg_str, FALSE, FALSE, log_msg_str, lmb->message_encoding, FALSE, pool, pool), _("Error normalizing log message to internal format")); *log_msg = log_msg_str->data; return SVN_NO_ERROR; } if (! commit_items->nelts) { *log_msg = ""; return SVN_NO_ERROR; } while (! message) { /* We still don't have a valid commit message. Use $EDITOR to get one. Note that svn_cl__edit_externally will still return a UTF-8'ized log message. */ int i; svn_stringbuf_t *tmp_message = svn_stringbuf_dup(default_msg, pool); svn_error_t *err = SVN_NO_ERROR; svn_string_t *msg_string = svn_string_create("", pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *); const char *path = item->path; char text_mod = '_', prop_mod = ' ', unlock = ' '; if (! path) path = item->url; else if (! *path) path = "."; if (! svn_path_is_url(path) && lmb->base_dir) path = svn_dirent_is_child(lmb->base_dir, path, pool); /* If still no path, then just use current directory. */ if (! path) path = "."; if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) && (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)) text_mod = 'R'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) text_mod = 'A'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE) text_mod = 'D'; else if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_TEXT_MODS) text_mod = 'M'; if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_PROP_MODS) prop_mod = 'M'; if (! lmb->keep_locks && item->state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN) unlock = 'U'; svn_stringbuf_appendbyte(tmp_message, text_mod); svn_stringbuf_appendbyte(tmp_message, prop_mod); svn_stringbuf_appendbyte(tmp_message, unlock); if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY) /* History included via copy/move. */ svn_stringbuf_appendcstr(tmp_message, "+ "); else svn_stringbuf_appendcstr(tmp_message, " "); svn_stringbuf_appendcstr(tmp_message, path); svn_stringbuf_appendcstr(tmp_message, APR_EOL_STR); } msg_string->data = tmp_message->data; msg_string->len = tmp_message->len; /* Use the external edit to get a log message. */ if (! lmb->non_interactive) { err = svn_cl__edit_string_externally(&msg_string, &lmb->tmpfile_left, lmb->editor_cmd, lmb->base_dir, msg_string, "svn-commit", lmb->config, TRUE, lmb->message_encoding, pool); } else /* non_interactive flag says we can't pop up an editor, so error */ { return svn_error_create (SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, _("Cannot invoke editor to get log message " "when non-interactive")); } /* Dup the tmpfile path into its baton's pool. */ *tmp_file = lmb->tmpfile_left = apr_pstrdup(lmb->pool, lmb->tmpfile_left); /* If the edit returned an error, handle it. */ if (err) { if (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR) err = svn_error_quick_wrap (err, _("Could not use external editor to fetch log message; " "consider setting the $SVN_EDITOR environment variable " "or using the --message (-m) or --file (-F) options")); return svn_error_trace(err); } if (msg_string) message = svn_stringbuf_create_from_string(msg_string, pool); /* Strip the prefix from the buffer. */ if (message) truncate_buffer_at_prefix(&message->len, message->data, EDITOR_EOF_PREFIX); if (message) { /* We did get message, now check if it is anything more than just white space as we will consider white space only as empty */ apr_size_t len; for (len = 0; len < message->len; len++) { /* FIXME: should really use an UTF-8 whitespace test rather than svn_ctype_isspace, which is ASCII only */ if (! svn_ctype_isspace(message->data[len])) break; } if (len == message->len) message = NULL; } if (! message) { const char *reply; SVN_ERR(svn_cmdline_prompt_user2 (&reply, _("\nLog message unchanged or not specified\n" "(a)bort, (c)ontinue, (e)dit:\n"), NULL, pool)); if (reply) { int letter = apr_tolower(reply[0]); /* If the user chooses to abort, we cleanup the temporary file and exit the loop with a NULL message. */ if ('a' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; break; } /* If the user chooses to continue, we make an empty message, which will cause us to exit the loop. We also cleanup the temporary file. */ if ('c' == letter) { SVN_ERR(svn_io_remove_file2(lmb->tmpfile_left, FALSE, pool)); *tmp_file = lmb->tmpfile_left = NULL; message = svn_stringbuf_create("", pool); } /* If the user chooses anything else, the loop will continue on the NULL message. */ } } } *log_msg = message ? message->data : NULL; return SVN_NO_ERROR; }