1/*
2 * log-cmd.c -- Display log messages
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24#define APR_WANT_STRFUNC
25#define APR_WANT_STDIO
26#include <apr_want.h>
27
28#include "svn_cmdline.h"
29#include "svn_compat.h"
30#include "svn_path.h"
31#include "svn_props.h"
32
33#include "cl.h"
34
35#include "svn_private_config.h"
36#include "private/svn_string_private.h"
37
38
39/*** Code. ***/
40
41/* Baton for log_entry_receiver() and log_entry_receiver_xml(). */
42struct log_receiver_baton
43{
44  /* Client context. */
45  svn_client_ctx_t *ctx;
46
47  /* Level of merge revision nesting */
48  apr_size_t merge_depth;
49
50  /* collect counters? */
51  svn_boolean_t quiet;
52
53  /* total revision counters */
54  apr_int64_t revisions;
55  apr_int64_t changes;
56  apr_int64_t message_lines;
57
58  /* part that came from merges */
59  apr_int64_t merges;
60  apr_int64_t merged_revs;
61  apr_int64_t merged_changes;
62  apr_int64_t merged_message_lines;
63};
64
65
66/* Implement `svn_log_entry_receiver_t', printing the logs in
67 * a human-readable and machine-parseable format.
68 *
69 * BATON is of type `struct log_receiver_baton'.
70 */
71static svn_error_t *
72log_entry_receiver(void *baton,
73                   svn_log_entry_t *log_entry,
74                   apr_pool_t *pool)
75{
76  struct log_receiver_baton *lb = baton;
77  const char *author;
78  const char *date;
79  const char *message;
80
81  if (lb->ctx->cancel_func)
82    SVN_ERR(lb->ctx->cancel_func(lb->ctx->cancel_baton));
83
84  if (! SVN_IS_VALID_REVNUM(log_entry->revision))
85    {
86      lb->merge_depth--;
87      return SVN_NO_ERROR;
88    }
89
90  /* if we don't want counters, we are done */
91  if (lb->quiet)
92    return SVN_NO_ERROR;
93
94  /* extract the message and do all the other counting */
95  svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops);
96  if (log_entry->revision == 0 && message == NULL)
97    return SVN_NO_ERROR;
98
99  lb->revisions++;
100  if (lb->merge_depth)
101    lb->merged_revs++;
102
103  if (message != NULL)
104    {
105      int count = svn_cstring_count_newlines(message) + 1;
106      lb->message_lines += count;
107      if (lb->merge_depth)
108        lb->merged_message_lines += count;
109    }
110
111  if (log_entry->changed_paths2)
112    {
113      unsigned count = apr_hash_count(log_entry->changed_paths2);
114      lb->changes += count;
115      if (lb->merge_depth)
116        lb->merged_changes += count;
117    }
118
119  if (log_entry->has_children)
120    {
121      lb->merge_depth++;
122      lb->merges++;
123    }
124
125  return SVN_NO_ERROR;
126}
127
128/* This implements the `svn_opt_subcommand_t' interface. */
129svn_error_t *
130svn_cl__null_log(apr_getopt_t *os,
131                 void *baton,
132                 apr_pool_t *pool)
133{
134  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
135  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
136  apr_array_header_t *targets;
137  struct log_receiver_baton lb = { 0 };
138  const char *target;
139  int i;
140  apr_array_header_t *revprops;
141  svn_opt_revision_t target_peg_revision;
142  const char *target_path_or_url;
143
144  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
145                                                      opt_state->targets,
146                                                      ctx, FALSE, pool));
147
148  /* Add "." if user passed 0 arguments */
149  svn_opt_push_implicit_dot_target(targets, pool);
150
151  /* Determine if they really want a two-revision range. */
152  if (opt_state->used_change_arg)
153    {
154      if (opt_state->used_revision_arg && opt_state->revision_ranges->nelts > 1)
155        {
156          return svn_error_create
157            (SVN_ERR_CLIENT_BAD_REVISION, NULL,
158             _("-c and -r are mutually exclusive"));
159        }
160      for (i = 0; i < opt_state->revision_ranges->nelts; i++)
161        {
162          svn_opt_revision_range_t *range;
163          range = APR_ARRAY_IDX(opt_state->revision_ranges, i,
164                                svn_opt_revision_range_t *);
165          if (range->start.value.number < range->end.value.number)
166            range->start.value.number++;
167          else
168            range->end.value.number++;
169        }
170    }
171
172  /* Parse the first target into path-or-url and peg revision. */
173  target = APR_ARRAY_IDX(targets, 0, const char *);
174  SVN_ERR(svn_opt_parse_path(&target_peg_revision, &target_path_or_url,
175                             target, pool));
176  if (target_peg_revision.kind == svn_opt_revision_unspecified)
177    target_peg_revision.kind = (svn_path_is_url(target)
178                                     ? svn_opt_revision_head
179                                     : svn_opt_revision_working);
180  APR_ARRAY_IDX(targets, 0, const char *) = target_path_or_url;
181
182  if (svn_path_is_url(target))
183    {
184      for (i = 1; i < targets->nelts; i++)
185        {
186          target = APR_ARRAY_IDX(targets, i, const char *);
187
188          if (svn_path_is_url(target) || target[0] == '/')
189            return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
190                                     _("Only relative paths can be specified"
191                                       " after a URL for 'svnbench log', "
192                                       "but '%s' is not a relative path"),
193                                     target);
194        }
195    }
196
197  lb.ctx = ctx;
198  lb.quiet = opt_state->quiet;
199
200  revprops = apr_array_make(pool, 3, sizeof(char *));
201  APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
202  APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
203  if (!opt_state->quiet)
204    APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
205  SVN_ERR(svn_client_log5(targets,
206                          &target_peg_revision,
207                          opt_state->revision_ranges,
208                          opt_state->limit,
209                          opt_state->verbose,
210                          opt_state->stop_on_copy,
211                          opt_state->use_merge_history,
212                          revprops,
213                          log_entry_receiver,
214                          &lb,
215                          ctx,
216                          pool));
217
218  if (!opt_state->quiet)
219    {
220      if (opt_state->use_merge_history)
221        SVN_ERR(svn_cmdline_printf(pool,
222                      _("%15s revisions, %15s merged in %s merges\n"
223                        "%15s msg lines, %15s in merged revisions\n"
224                        "%15s changes,   %15s in merged revisions\n"),
225                      svn__ui64toa_sep(lb.revisions, ',', pool),
226                      svn__ui64toa_sep(lb.merged_revs, ',', pool),
227                      svn__ui64toa_sep(lb.merges, ',', pool),
228                      svn__ui64toa_sep(lb.message_lines, ',', pool),
229                      svn__ui64toa_sep(lb.merged_message_lines, ',', pool),
230                      svn__ui64toa_sep(lb.changes, ',', pool),
231                      svn__ui64toa_sep(lb.merged_changes, ',', pool)));
232      else
233        SVN_ERR(svn_cmdline_printf(pool,
234                      _("%15s revisions\n"
235                        "%15s msg lines\n"
236                        "%15s changes\n"),
237                      svn__ui64toa_sep(lb.revisions, ',', pool),
238                      svn__ui64toa_sep(lb.message_lines, ',', pool),
239                      svn__ui64toa_sep(lb.changes, ',', pool)));
240    }
241
242  return SVN_NO_ERROR;
243}
244