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