~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_ra_dav/log.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * log.c :  routines for requesting and parsing log reports
 
3
 *
 
4
 * ====================================================================
 
5
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
6
 *
 
7
 * This software is licensed as described in the file COPYING, which
 
8
 * you should have received as part of this distribution.  The terms
 
9
 * are also available at http://subversion.tigris.org/license-1.html.
 
10
 * If newer versions of this license are posted there, you may use a
 
11
 * newer version instead, at your option.
 
12
 *
 
13
 * This software consists of voluntary contributions made by many
 
14
 * individuals.  For exact contribution history, see the revision
 
15
 * history and logs, available at http://subversion.tigris.org/.
 
16
 * ====================================================================
 
17
 */
 
18
 
 
19
 
 
20
 
 
21
#define APR_WANT_STRFUNC
 
22
#include <apr_want.h> /* for strcmp() */
 
23
 
 
24
#include <apr_pools.h>
 
25
#include <apr_tables.h>
 
26
#include <apr_strings.h>
 
27
#include <apr_xml.h>
 
28
 
 
29
#include <ne_socket.h>
 
30
 
 
31
#include "svn_error.h"
 
32
#include "svn_pools.h"
 
33
#include "svn_path.h"
 
34
#include "svn_xml.h"
 
35
#include "../libsvn_ra/ra_loader.h"
 
36
 
 
37
#include "ra_dav.h"
 
38
 
 
39
 
 
40
 
 
41
/*** Code ***/
 
42
 
 
43
/* Userdata for the Neon XML element callbacks. */
 
44
struct log_baton
 
45
{
 
46
  /* Allocate log message information.
 
47
   * NOTE: this pool may be cleared multiple times as log messages are
 
48
   * received.
 
49
   */
 
50
  apr_pool_t *subpool;
 
51
 
 
52
  /* Information about each log item in turn. */
 
53
  svn_revnum_t revision;
 
54
  const char *author;
 
55
  const char *date;
 
56
  const char *msg;
 
57
 
 
58
  /* Keys are the paths changed in this commit, allocated in SUBPOOL;
 
59
     the table itself is also allocated in SUBPOOL.  If this table is
 
60
     NULL, no changed paths were indicated -- which doesn't mean no
 
61
     paths were changed, just means that this log invocation didn't
 
62
     ask for them to be reported. */
 
63
  apr_hash_t *changed_paths;
 
64
 
 
65
  /* The current changed path item. */
 
66
  svn_log_changed_path_t *this_path_item;
 
67
 
 
68
  /* Client's callback, invoked on the above fields when the end of an
 
69
     item is seen. */
 
70
  svn_log_message_receiver_t receiver;
 
71
  void *receiver_baton;
 
72
 
 
73
  int limit;
 
74
  int count;
 
75
 
 
76
  /* If `receiver' returns error, it is stored here. */
 
77
  svn_error_t *err;
 
78
};
 
79
 
 
80
 
 
81
/* Prepare LB to start accumulating the next log item, by wiping all
 
82
 * information related to the previous item and clearing the pool in
 
83
 * which they were allocated.  Do not touch any stored error, however.
 
84
 */
 
85
static void
 
86
reset_log_item (struct log_baton *lb)
 
87
{
 
88
  lb->revision      = SVN_INVALID_REVNUM;
 
89
  lb->author        = NULL;
 
90
  lb->date          = NULL;
 
91
  lb->msg           = NULL;
 
92
  lb->changed_paths = NULL;
 
93
 
 
94
  svn_pool_clear (lb->subpool);
 
95
}
 
96
 
 
97
 
 
98
/*
 
99
 * This implements the `svn_ra_dav__xml_validate_cb' prototype.
 
100
 */
 
101
static int
 
102
log_validate(void *userdata, svn_ra_dav__xml_elmid parent,
 
103
             svn_ra_dav__xml_elmid child)
 
104
{
 
105
  /* ### todo */
 
106
  return SVN_RA_DAV__XML_VALID;
 
107
}
 
108
 
 
109
/*
 
110
 * This implements the `svn_ra_dav__xml_startelm_cb' prototype.
 
111
 */
 
112
static int
 
113
log_start_element(void *userdata,
 
114
                  const svn_ra_dav__xml_elm_t *elm,
 
115
                  const char **atts)
 
116
{
 
117
  struct log_baton *lb = userdata;
 
118
  const char *copyfrom_path, *copyfrom_revstr;
 
119
  svn_revnum_t copyfrom_rev;
 
120
 
 
121
  switch (elm->id)
 
122
    {
 
123
    case ELEM_added_path:
 
124
    case ELEM_replaced_path:
 
125
    case ELEM_deleted_path:
 
126
    case ELEM_modified_path:
 
127
      lb->this_path_item = apr_pcalloc(lb->subpool, 
 
128
                                       sizeof(*(lb->this_path_item)));
 
129
      lb->this_path_item->copyfrom_rev = SVN_INVALID_REVNUM;
 
130
 
 
131
      /* See documentation for `svn_repos_node_t' in svn_repos.h,
 
132
         and `svn_log_message_receiver_t' in svn_types.h, for more
 
133
         about these action codes. */
 
134
      if ((elm->id == ELEM_added_path) || (elm->id == ELEM_replaced_path))
 
135
        {
 
136
          lb->this_path_item->action 
 
137
            = (elm->id == ELEM_added_path) ? 'A' : 'R';
 
138
          copyfrom_path = svn_xml_get_attr_value("copyfrom-path", atts);
 
139
          copyfrom_revstr = svn_xml_get_attr_value("copyfrom-rev", atts);
 
140
          if (copyfrom_path && copyfrom_revstr
 
141
              && (SVN_IS_VALID_REVNUM
 
142
                  (copyfrom_rev = SVN_STR_TO_REV (copyfrom_revstr))))
 
143
            {
 
144
              lb->this_path_item->copyfrom_path = apr_pstrdup(lb->subpool,
 
145
                                                              copyfrom_path);
 
146
              lb->this_path_item->copyfrom_rev = copyfrom_rev;
 
147
            }
 
148
        }
 
149
      else if (elm->id == ELEM_deleted_path)
 
150
        {
 
151
          lb->this_path_item->action = 'D';
 
152
        }
 
153
      else
 
154
        {
 
155
          lb->this_path_item->action = 'M';
 
156
        }
 
157
      break;
 
158
 
 
159
    default:
 
160
      lb->this_path_item = NULL;
 
161
      break;
 
162
    }
 
163
  return SVN_RA_DAV__XML_VALID;
 
164
}
 
165
 
 
166
 
 
167
/*
 
168
 * This implements the `svn_ra_dav__xml_endelm_cb' prototype.
 
169
 */
 
170
static int
 
171
log_end_element(void *userdata,
 
172
                const svn_ra_dav__xml_elm_t *elm,
 
173
                const char *cdata)
 
174
{
 
175
  struct log_baton *lb = userdata;
 
176
 
 
177
  switch (elm->id)
 
178
    {
 
179
    case ELEM_version_name:
 
180
      lb->revision = SVN_STR_TO_REV (cdata);
 
181
      break;
 
182
    case ELEM_creator_displayname:
 
183
      lb->author = apr_pstrdup (lb->subpool, cdata);
 
184
      break;
 
185
    case ELEM_log_date:
 
186
      lb->date = apr_pstrdup (lb->subpool, cdata);
 
187
      break;
 
188
    case ELEM_added_path:
 
189
    case ELEM_replaced_path:
 
190
    case ELEM_deleted_path:
 
191
    case ELEM_modified_path:
 
192
      {
 
193
        char *path = apr_pstrdup (lb->subpool, cdata);
 
194
        if (! lb->changed_paths)
 
195
          lb->changed_paths = apr_hash_make(lb->subpool);
 
196
        apr_hash_set(lb->changed_paths, path, APR_HASH_KEY_STRING, 
 
197
                     lb->this_path_item);
 
198
        break;
 
199
      }
 
200
    case ELEM_comment:
 
201
      lb->msg = apr_pstrdup (lb->subpool, cdata);
 
202
      break;
 
203
    case ELEM_log_item:
 
204
      {
 
205
        svn_error_t *err;
 
206
        /* Compatability cruft so that we can provide limit functionality 
 
207
           even if the server doesn't support it.
 
208
 
 
209
           If we've seen as many log entries as we're going to show just
 
210
           error out of the XML parser so we can avoid having to parse the
 
211
           remaining XML, but set lb->err to SVN_NO_ERROR so no error will
 
212
           end up being shown to the user. */
 
213
        if (lb->limit && (++lb->count > lb->limit))
 
214
          {
 
215
            lb->err = SVN_NO_ERROR;
 
216
            return SVN_RA_DAV__XML_INVALID;
 
217
          }
 
218
 
 
219
        err = (*(lb->receiver))(lb->receiver_baton,
 
220
                                             lb->changed_paths,
 
221
                                             lb->revision,
 
222
                                             lb->author,
 
223
                                             lb->date,
 
224
                                             lb->msg,
 
225
                                             lb->subpool);
 
226
 
 
227
        reset_log_item (lb);
 
228
        
 
229
        if (err)
 
230
          {
 
231
            /* Only remember the first error. */
 
232
            if (lb->err == NULL)
 
233
              lb->err = err;
 
234
            else
 
235
              svn_error_clear(err);
 
236
              
 
237
            return SVN_RA_DAV__XML_INVALID; /* ### Any other way to express
 
238
                                                   an err? */
 
239
          }
 
240
      }
 
241
      break;
 
242
    case ELEM_log_report:
 
243
      {
 
244
        /* Do nothing.  But...
 
245
         *
 
246
         * ### Possibility:
 
247
         *
 
248
         * Greg Stein mused that we could treat log_receivers the way
 
249
         * we treat delta window consumers -- "no more calls" would be
 
250
         * indicated by a special last call that passes
 
251
         * SVN_INVALID_REVNUM as the revision number.  That would work
 
252
         * fine, but right now most of the code just handles the
 
253
         * first-call/last-call thing by having one-time code on
 
254
         * either side of the iterator, which works just as well.
 
255
         *
 
256
         * I don't feel any compelling need to change this right now.
 
257
         * If we do change it, the hot spots are:
 
258
         *
 
259
         *    - libsvn_repos/log.c:
 
260
         *         svn_repos_get_logs() would need a new post-loop
 
261
         *         call to (*receiver)(), passing SVN_INVALID_REVNUM.
 
262
         *         Make sure not to destroy that subpool until
 
263
         *         after the new call! :-)
 
264
         *
 
265
         *    - mod_dav_svn/log.c:
 
266
         *        `struct log_receiver_baton' would need a first_call
 
267
         *         flag; dav_svn__log_report() would set it up, and
 
268
         *         then log_receiver() would be responsible for
 
269
         *         emitting "<S:log-report>" and "</S:log-report>"
 
270
         *         instead.
 
271
         *
 
272
         *    - clients/cmdline/log-cmd.c:
 
273
         *         svn_cl__log() would no longer be responsible for
 
274
         *         emitting the "<log>" and "</log>" elements.  The
 
275
         *         body of this function would get a lot simpler, mmm!
 
276
         *         Instead, log_message_receiver_xml() would pay
 
277
         *         attention to baton->first_call, and handle
 
278
         *         SVN_INVALID_REVNUM, to emit those elements
 
279
         *         instead.  The old log_message_receiver() function
 
280
         *         wouldn't need to change at all, though, I think.
 
281
         *
 
282
         *    - Right here:
 
283
         *      We'd have a new call to (*(lb->receiver)), passing
 
284
         *      SVN_INVALID_REVNUM, of course.
 
285
         *
 
286
         * There, I think that's the change.  Thoughts? :-)
 
287
         */
 
288
      }
 
289
      break;
 
290
    }
 
291
 
 
292
  return SVN_RA_DAV__XML_VALID;
 
293
}
 
294
 
 
295
 
 
296
svn_error_t * svn_ra_dav__get_log(svn_ra_session_t *session,
 
297
                                  const apr_array_header_t *paths,
 
298
                                  svn_revnum_t start,
 
299
                                  svn_revnum_t end,
 
300
                                  int limit,
 
301
                                  svn_boolean_t discover_changed_paths,
 
302
                                  svn_boolean_t strict_node_history,
 
303
                                  svn_log_message_receiver_t receiver,
 
304
                                  void *receiver_baton,
 
305
                                  apr_pool_t *pool)
 
306
{
 
307
  /* The Plan: Send a request to the server for a log report.
 
308
   * Somewhere in mod_dav_svn, there will be an implementation, R, of
 
309
   * the `svn_log_message_receiver_t' function type.  Some other
 
310
   * function in mod_dav_svn will use svn_repos_get_logs() to loop R
 
311
   * over the log messages, and the successive invocations of R will
 
312
   * collectively transmit the report back here, where we parse the
 
313
   * report and invoke RECEIVER (which is an entirely separate
 
314
   * instance of `svn_log_message_receiver_t') on each individual
 
315
   * message in that report.
 
316
   */
 
317
 
 
318
  int i;
 
319
  svn_ra_dav__session_t *ras = session->priv;
 
320
  svn_stringbuf_t *request_body = svn_stringbuf_create("", ras->pool);
 
321
  struct log_baton lb;
 
322
  svn_string_t bc_url, bc_relative;
 
323
  const char *final_bc_url;
 
324
  svn_revnum_t use_rev;
 
325
 
 
326
  /* ### todo: I don't understand why the static, file-global
 
327
     variables shared by update and status are called `report_head'
 
328
     and `report_tail', instead of `request_head' and `request_tail'.
 
329
     Maybe Greg can explain?  Meanwhile, I'm tentatively using
 
330
     "request_*" for my local vars below. */
 
331
 
 
332
  static const char log_request_head[]
 
333
    = "<S:log-report xmlns:S=\"" SVN_XML_NAMESPACE "\">" DEBUG_CR;
 
334
 
 
335
  static const char log_request_tail[] = "</S:log-report>" DEBUG_CR;
 
336
  
 
337
  static const svn_ra_dav__xml_elm_t log_report_elements[] =
 
338
    {
 
339
      { SVN_XML_NAMESPACE, "log-report", ELEM_log_report, 0 },
 
340
      { SVN_XML_NAMESPACE, "log-item", ELEM_log_item, 0 },
 
341
      { SVN_XML_NAMESPACE, "date", ELEM_log_date, SVN_RA_DAV__XML_CDATA },
 
342
      { SVN_XML_NAMESPACE, "added-path", ELEM_added_path,
 
343
        SVN_RA_DAV__XML_CDATA },
 
344
      { SVN_XML_NAMESPACE, "deleted-path", ELEM_deleted_path,
 
345
        SVN_RA_DAV__XML_CDATA },
 
346
      { SVN_XML_NAMESPACE, "modified-path", ELEM_modified_path,
 
347
        SVN_RA_DAV__XML_CDATA },
 
348
      { SVN_XML_NAMESPACE, "replaced-path", ELEM_replaced_path,
 
349
        SVN_RA_DAV__XML_CDATA },
 
350
      { "DAV:", "version-name", ELEM_version_name, SVN_RA_DAV__XML_CDATA },
 
351
      { "DAV:", "creator-displayname", ELEM_creator_displayname,
 
352
        SVN_RA_DAV__XML_CDATA },
 
353
      { "DAV:", "comment", ELEM_comment, SVN_RA_DAV__XML_CDATA },
 
354
      { NULL }
 
355
    };
 
356
  
 
357
 
 
358
  /* Construct the request body. */
 
359
  svn_stringbuf_appendcstr(request_body, log_request_head);
 
360
  svn_stringbuf_appendcstr(request_body,
 
361
                           apr_psprintf(ras->pool,
 
362
                                        "<S:start-revision>%ld"
 
363
                                        "</S:start-revision>", start));
 
364
  svn_stringbuf_appendcstr(request_body,
 
365
                           apr_psprintf(ras->pool,
 
366
                                        "<S:end-revision>%ld"
 
367
                                        "</S:end-revision>", end));
 
368
  if (limit)
 
369
    {
 
370
      svn_stringbuf_appendcstr(request_body,
 
371
                               apr_psprintf(ras->pool,
 
372
                                            "<S:limit>%d</S:limit>", limit));
 
373
    }
 
374
 
 
375
  if (discover_changed_paths)
 
376
    {
 
377
      svn_stringbuf_appendcstr(request_body,
 
378
                               apr_psprintf(ras->pool,
 
379
                                            "<S:discover-changed-paths/>"));
 
380
    }
 
381
 
 
382
  if (strict_node_history)
 
383
    {
 
384
      svn_stringbuf_appendcstr(request_body,
 
385
                               apr_psprintf(ras->pool,
 
386
                                            "<S:strict-node-history/>"));
 
387
    }
 
388
 
 
389
  if (paths)
 
390
    {
 
391
      for (i = 0; i < paths->nelts; i++)
 
392
        {
 
393
          const char *this_path =
 
394
            apr_xml_quote_string(ras->pool,
 
395
                                 ((const char **)paths->elts)[i],
 
396
                                 0);
 
397
          svn_stringbuf_appendcstr(request_body, "<S:path>");
 
398
          svn_stringbuf_appendcstr(request_body, this_path);
 
399
          svn_stringbuf_appendcstr(request_body, "</S:path>");
 
400
        }
 
401
    }
 
402
 
 
403
  svn_stringbuf_appendcstr(request_body, log_request_tail);
 
404
 
 
405
  lb.receiver = receiver;
 
406
  lb.receiver_baton = receiver_baton;
 
407
  lb.subpool = svn_pool_create (ras->pool);
 
408
  lb.err = NULL;
 
409
  lb.limit = limit;
 
410
  lb.count = 0;
 
411
  reset_log_item (&lb);
 
412
 
 
413
  /* ras's URL may not exist in HEAD, and thus it's not safe to send
 
414
     it as the main argument to the REPORT request; it might cause
 
415
     dav_get_resource() to choke on the server.  So instead, we pass a
 
416
     baseline-collection URL, which we get from the largest of the
 
417
     START and END revisions. */
 
418
  use_rev = (start > end) ? start : end;
 
419
  SVN_ERR( svn_ra_dav__get_baseline_info(NULL, &bc_url, &bc_relative, NULL,
 
420
                                         ras->sess, ras->url, use_rev,
 
421
                                         ras->pool) );
 
422
  final_bc_url = svn_path_url_add_component(bc_url.data, bc_relative.data,
 
423
                                            ras->pool);
 
424
 
 
425
 
 
426
  SVN_ERR( svn_ra_dav__parsed_request_compat(ras->sess,
 
427
                                             "REPORT",
 
428
                                             final_bc_url,
 
429
                                             request_body->data,
 
430
                                             0,  /* ignored */
 
431
                                             NULL,
 
432
                                             log_report_elements, 
 
433
                                             log_validate,
 
434
                                             log_start_element,
 
435
                                             log_end_element,
 
436
                                             &lb,
 
437
                                             NULL, 
 
438
                                             NULL,
 
439
                                             FALSE,
 
440
                                             ras->pool) );
 
441
 
 
442
  if (lb.err)
 
443
    return lb.err;
 
444
 
 
445
  svn_pool_destroy (lb.subpool);
 
446
 
 
447
  return SVN_NO_ERROR;
 
448
}