1289177Speter/* 2289177Speter * log-cmd.c -- Display log messages 3289177Speter * 4289177Speter * ==================================================================== 5289177Speter * Licensed to the Apache Software Foundation (ASF) under one 6289177Speter * or more contributor license agreements. See the NOTICE file 7289177Speter * distributed with this work for additional information 8289177Speter * regarding copyright ownership. The ASF licenses this file 9289177Speter * to you under the Apache License, Version 2.0 (the 10289177Speter * "License"); you may not use this file except in compliance 11289177Speter * with the License. You may obtain a copy of the License at 12289177Speter * 13289177Speter * http://www.apache.org/licenses/LICENSE-2.0 14289177Speter * 15289177Speter * Unless required by applicable law or agreed to in writing, 16289177Speter * software distributed under the License is distributed on an 17289177Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18289177Speter * KIND, either express or implied. See the License for the 19289177Speter * specific language governing permissions and limitations 20289177Speter * under the License. 21289177Speter * ==================================================================== 22289177Speter */ 23289177Speter 24289177Speter#define APR_WANT_STRFUNC 25289177Speter#define APR_WANT_STDIO 26289177Speter#include <apr_want.h> 27289177Speter 28289177Speter#include "svn_cmdline.h" 29289177Speter#include "svn_compat.h" 30289177Speter#include "svn_path.h" 31289177Speter#include "svn_props.h" 32289177Speter 33289177Speter#include "cl.h" 34289177Speter 35289177Speter#include "svn_private_config.h" 36289177Speter#include "private/svn_string_private.h" 37289177Speter 38289177Speter 39289177Speter/*** Code. ***/ 40289177Speter 41289177Speter/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */ 42289177Speterstruct log_receiver_baton 43289177Speter{ 44289177Speter /* Client context. */ 45289177Speter svn_client_ctx_t *ctx; 46289177Speter 47289177Speter /* Level of merge revision nesting */ 48289177Speter apr_size_t merge_depth; 49289177Speter 50289177Speter /* collect counters? */ 51289177Speter svn_boolean_t quiet; 52289177Speter 53289177Speter /* total revision counters */ 54289177Speter apr_int64_t revisions; 55289177Speter apr_int64_t changes; 56289177Speter apr_int64_t message_lines; 57289177Speter 58289177Speter /* part that came from merges */ 59289177Speter apr_int64_t merges; 60289177Speter apr_int64_t merged_revs; 61289177Speter apr_int64_t merged_changes; 62289177Speter apr_int64_t merged_message_lines; 63289177Speter}; 64289177Speter 65289177Speter 66289177Speter/* Implement `svn_log_entry_receiver_t', printing the logs in 67289177Speter * a human-readable and machine-parseable format. 68289177Speter * 69289177Speter * BATON is of type `struct log_receiver_baton'. 70289177Speter */ 71289177Speterstatic svn_error_t * 72289177Speterlog_entry_receiver(void *baton, 73289177Speter svn_log_entry_t *log_entry, 74289177Speter apr_pool_t *pool) 75289177Speter{ 76289177Speter struct log_receiver_baton *lb = baton; 77289177Speter const char *author; 78289177Speter const char *date; 79289177Speter const char *message; 80289177Speter 81289177Speter if (lb->ctx->cancel_func) 82289177Speter SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton)); 83289177Speter 84289177Speter if (! SVN_IS_VALID_REVNUM(log_entry->revision)) 85289177Speter { 86289177Speter lb->merge_depth--; 87289177Speter return SVN_NO_ERROR; 88289177Speter } 89289177Speter 90289177Speter /* if we don't want counters, we are done */ 91289177Speter if (lb->quiet) 92289177Speter return SVN_NO_ERROR; 93289177Speter 94289177Speter /* extract the message and do all the other counting */ 95289177Speter svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops); 96289177Speter if (log_entry->revision == 0 && message == NULL) 97289177Speter return SVN_NO_ERROR; 98289177Speter 99289177Speter lb->revisions++; 100289177Speter if (lb->merge_depth) 101289177Speter lb->merged_revs++; 102289177Speter 103289177Speter if (message != NULL) 104289177Speter { 105289177Speter int count = svn_cstring_count_newlines(message) + 1; 106289177Speter lb->message_lines += count; 107289177Speter if (lb->merge_depth) 108289177Speter lb->merged_message_lines += count; 109289177Speter } 110289177Speter 111289177Speter if (log_entry->changed_paths2) 112289177Speter { 113289177Speter unsigned count = apr_hash_count(log_entry->changed_paths2); 114289177Speter lb->changes += count; 115289177Speter if (lb->merge_depth) 116289177Speter lb->merged_changes += count; 117289177Speter } 118289177Speter 119289177Speter if (log_entry->has_children) 120289177Speter { 121289177Speter lb->merge_depth++; 122289177Speter lb->merges++; 123289177Speter } 124289177Speter 125289177Speter return SVN_NO_ERROR; 126289177Speter} 127289177Speter 128289177Speter/* This implements the `svn_opt_subcommand_t' interface. */ 129289177Spetersvn_error_t * 130289177Spetersvn_cl__null_log(apr_getopt_t *os, 131289177Speter void *baton, 132289177Speter apr_pool_t *pool) 133289177Speter{ 134289177Speter svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 135289177Speter svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 136289177Speter apr_array_header_t *targets; 137289177Speter struct log_receiver_baton lb = { 0 }; 138289177Speter const char *target; 139289177Speter int i; 140289177Speter apr_array_header_t *revprops; 141289177Speter svn_opt_revision_t target_peg_revision; 142289177Speter const char *target_path_or_url; 143289177Speter 144289177Speter SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 145289177Speter opt_state->targets, 146289177Speter ctx, FALSE, pool)); 147289177Speter 148289177Speter /* Add "." if user passed 0 arguments */ 149289177Speter svn_opt_push_implicit_dot_target(targets, pool); 150289177Speter 151289177Speter /* Determine if they really want a two-revision range. */ 152289177Speter if (opt_state->used_change_arg) 153289177Speter { 154289177Speter if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1) 155289177Speter { 156289177Speter return svn_error_create 157289177Speter (SVN_ERR_CLIENT_BAD_REVISION, NULL, 158289177Speter _("-c and -r are mutually exclusive")); 159289177Speter } 160289177Speter for (i = 0; i < opt_state->revision_ranges->nelts; i++) 161289177Speter { 162289177Speter svn_opt_revision_range_t *range; 163289177Speter range = APR_ARRAY_IDX(opt_state->revision_ranges, i, 164289177Speter svn_opt_revision_range_t *); 165289177Speter if (range->start.value.number < range->end.value.number) 166289177Speter range->start.value.number++; 167289177Speter else 168289177Speter range->end.value.number++; 169289177Speter } 170289177Speter } 171289177Speter 172289177Speter /* Parse the first target into path-or-url and peg revision. */ 173289177Speter target = APR_ARRAY_IDX(targets, 0, const char *); 174289177Speter SVN_ERR(svn_opt_parse_path(&target_peg_revision, &target_path_or_url, 175289177Speter target, pool)); 176289177Speter if (target_peg_revision.kind == svn_opt_revision_unspecified) 177289177Speter target_peg_revision.kind = (svn_path_is_url(target) 178289177Speter ? svn_opt_revision_head 179289177Speter : svn_opt_revision_working); 180289177Speter APR_ARRAY_IDX(targets, 0, const char *) = target_path_or_url; 181289177Speter 182289177Speter if (svn_path_is_url(target)) 183289177Speter { 184289177Speter for (i = 1; i < targets->nelts; i++) 185289177Speter { 186289177Speter target = APR_ARRAY_IDX(targets, i, const char *); 187289177Speter 188289177Speter if (svn_path_is_url(target) || target[0] == '/') 189289177Speter return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 190289177Speter _("Only relative paths can be specified" 191289177Speter " after a URL for 'svnbench log', " 192289177Speter "but '%s' is not a relative path"), 193289177Speter target); 194289177Speter } 195289177Speter } 196289177Speter 197289177Speter lb.ctx = ctx; 198289177Speter lb.quiet = opt_state->quiet; 199289177Speter 200289177Speter revprops = apr_array_make(pool, 3, sizeof(char *)); 201289177Speter APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR; 202289177Speter APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE; 203289177Speter if (!opt_state->quiet) 204289177Speter APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG; 205289177Speter SVN_ERR(svn_client_log5(targets, 206289177Speter &target_peg_revision, 207289177Speter opt_state->revision_ranges, 208289177Speter opt_state->limit, 209289177Speter opt_state->verbose, 210289177Speter opt_state->stop_on_copy, 211289177Speter opt_state->use_merge_history, 212289177Speter revprops, 213289177Speter log_entry_receiver, 214289177Speter &lb, 215289177Speter ctx, 216289177Speter pool)); 217289177Speter 218289177Speter if (!opt_state->quiet) 219289177Speter { 220289177Speter if (opt_state->use_merge_history) 221289177Speter SVN_ERR(svn_cmdline_printf(pool, 222289177Speter _("%15s revisions, %15s merged in %s merges\n" 223289177Speter "%15s msg lines, %15s in merged revisions\n" 224289177Speter "%15s changes, %15s in merged revisions\n"), 225289177Speter svn__ui64toa_sep(lb.revisions, ',', pool), 226289177Speter svn__ui64toa_sep(lb.merged_revs, ',', pool), 227289177Speter svn__ui64toa_sep(lb.merges, ',', pool), 228289177Speter svn__ui64toa_sep(lb.message_lines, ',', pool), 229289177Speter svn__ui64toa_sep(lb.merged_message_lines, ',', pool), 230289177Speter svn__ui64toa_sep(lb.changes, ',', pool), 231289177Speter svn__ui64toa_sep(lb.merged_changes, ',', pool))); 232289177Speter else 233289177Speter SVN_ERR(svn_cmdline_printf(pool, 234289177Speter _("%15s revisions\n" 235289177Speter "%15s msg lines\n" 236289177Speter "%15s changes\n"), 237289177Speter svn__ui64toa_sep(lb.revisions, ',', pool), 238289177Speter svn__ui64toa_sep(lb.message_lines, ',', pool), 239289177Speter svn__ui64toa_sep(lb.changes, ',', pool))); 240289177Speter } 241289177Speter 242289177Speter return SVN_NO_ERROR; 243289177Speter} 244