~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/svnrdump/dump_editor.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
#include "svn_subst.h"
31
31
#include "svn_dirent_uri.h"
32
32
 
33
 
#include "private/svn_fspath.h"
 
33
#include "private/svn_subr_private.h"
 
34
#include "private/svn_dep_compat.h"
 
35
#include "private/svn_editor.h"
34
36
 
35
37
#include "svnrdump.h"
 
38
#include <assert.h>
36
39
 
37
40
#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
38
41
 
49
52
  struct dump_edit_baton *eb;
50
53
  struct dir_baton *parent_dir_baton;
51
54
 
 
55
  /* Pool for per-directory allocations */
 
56
  apr_pool_t *pool;
 
57
 
52
58
  /* is this directory a new addition to this revision? */
53
59
  svn_boolean_t added;
54
60
 
55
61
  /* has this directory been written to the output stream? */
56
62
  svn_boolean_t written_out;
57
63
 
58
 
  /* the absolute path to this directory */
59
 
  const char *abspath; /* an fspath */
 
64
  /* the path to this directory */
 
65
  const char *repos_relpath; /* a relpath */
60
66
 
61
67
  /* Copyfrom info for the node, if any. */
62
68
  const char *copyfrom_path; /* a relpath */
63
69
  svn_revnum_t copyfrom_rev;
64
70
 
 
71
  /* Properties which were modified during change_dir_prop. */
 
72
  apr_hash_t *props;
 
73
 
 
74
  /* Properties which were deleted during change_dir_prop. */
 
75
  apr_hash_t *deleted_props;
 
76
 
65
77
  /* Hash of paths that need to be deleted, though some -might- be
66
78
     replaced.  Maps const char * paths to this dir_baton. Note that
67
79
     they're full paths, because that's what the editor driver gives
68
80
     us, although they're all really within this directory. */
69
81
  apr_hash_t *deleted_entries;
 
82
 
 
83
  /* Flags to trigger dumping props and record termination newlines. */
 
84
  svn_boolean_t dump_props;
 
85
  svn_boolean_t dump_newlines;
 
86
};
 
87
 
 
88
/* A file baton used by all file-related callback functions in the dump
 
89
 * editor */
 
90
struct file_baton
 
91
{
 
92
  struct dump_edit_baton *eb;
 
93
  struct dir_baton *parent_dir_baton;
 
94
 
 
95
  /* Pool for per-file allocations */
 
96
  apr_pool_t *pool;
 
97
 
 
98
  /* the path to this file */
 
99
  const char *repos_relpath; /* a relpath */
 
100
 
 
101
  /* Properties which were modified during change_file_prop. */
 
102
  apr_hash_t *props;
 
103
 
 
104
  /* Properties which were deleted during change_file_prop. */
 
105
  apr_hash_t *deleted_props;
 
106
 
 
107
  /* The checksum of the file the delta is being applied to */
 
108
  const char *base_checksum;
 
109
 
 
110
  /* Copy state and source information (if any). */
 
111
  svn_boolean_t is_copy;
 
112
  const char *copyfrom_path;
 
113
  svn_revnum_t copyfrom_rev;
 
114
 
 
115
  /* The action associate with this node. */
 
116
  enum svn_node_action action;
 
117
 
 
118
  /* Flags to trigger dumping props and text. */
 
119
  svn_boolean_t dump_text;
 
120
  svn_boolean_t dump_props;
70
121
};
71
122
 
72
123
/* A handler baton to be used in window_handler().  */
81
132
  /* The output stream we write the dumpfile to */
82
133
  svn_stream_t *stream;
83
134
 
 
135
  /* A backdoor ra session to fetch additional information during the edit. */
 
136
  svn_ra_session_t *ra_session;
 
137
 
 
138
  /* The repository relpath of the anchor of the editor when driven
 
139
     via the RA update mechanism; NULL otherwise. (When the editor is
 
140
     driven via the RA "replay" mechanism instead, the editor is
 
141
     always anchored at the repository, we don't need to prepend an
 
142
     anchor path to the dumped node paths, and open_root() doesn't
 
143
     need to manufacture directory additions.)  */
 
144
  const char *update_anchor_relpath;
 
145
 
84
146
  /* Pool for per-revision allocations */
85
147
  apr_pool_t *pool;
86
148
 
87
 
  /* Properties which were modified during change_file_prop
88
 
   * or change_dir_prop. */
89
 
  apr_hash_t *props;
90
 
 
91
 
  /* Properties which were deleted during change_file_prop
92
 
   * or change_dir_prop. */
93
 
  apr_hash_t *deleted_props;
94
 
 
95
 
  /* Temporary buffer to write property hashes to in human-readable
96
 
   * form. ### Is this really needed? */
97
 
  svn_stringbuf_t *propstring;
98
 
 
99
149
  /* Temporary file used for textdelta application along with its
100
150
     absolute path; these two variables should be allocated in the
101
151
     per-edit-session pool */
102
152
  const char *delta_abspath;
103
153
  apr_file_t *delta_file;
104
154
 
105
 
  /* The checksum of the file the delta is being applied to */
106
 
  const char *base_checksum;
 
155
  /* The revision we're currently dumping. */
 
156
  svn_revnum_t current_revision;
107
157
 
108
 
  /* Flags to trigger dumping props and text */
109
 
  svn_boolean_t dump_text;
110
 
  svn_boolean_t dump_props;
111
 
  svn_boolean_t dump_newlines;
 
158
  /* The kind (file or directory) and baton of the item whose block of
 
159
     dump stream data has not been fully completed; NULL if there's no
 
160
     such item. */
 
161
  svn_node_kind_t pending_kind;
 
162
  void *pending_baton;
112
163
};
113
164
 
114
165
/* Make a directory baton to represent the directory at PATH (relative
119
170
 * information is valid, the directory will be compared against its
120
171
 * copy source.
121
172
 *
122
 
 * PARENT_DIR_BATON is the directory baton of this directory's parent,
123
 
 * or NULL if this is the top-level directory of the edit.  ADDED
124
 
 * indicates if this directory is newly added in this revision.
125
 
 * Perform all allocations in POOL.  */
 
173
 * PB is the directory baton of this directory's parent, or NULL if
 
174
 * this is the top-level directory of the edit.  ADDED indicates if
 
175
 * this directory is newly added in this revision.  Perform all
 
176
 * allocations in POOL.  */
126
177
static struct dir_baton *
127
178
make_dir_baton(const char *path,
128
179
               const char *copyfrom_path,
129
180
               svn_revnum_t copyfrom_rev,
130
181
               void *edit_baton,
131
 
               void *parent_dir_baton,
 
182
               struct dir_baton *pb,
132
183
               svn_boolean_t added,
133
184
               apr_pool_t *pool)
134
185
{
135
186
  struct dump_edit_baton *eb = edit_baton;
136
 
  struct dir_baton *pb = parent_dir_baton;
137
187
  struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
138
 
  const char *abspath;
 
188
  const char *repos_relpath;
139
189
 
140
190
  /* Construct the full path of this node. */
141
 
  /* ### FIXME: Not sure why we use an abspath here.  If I understand
142
 
     ### correctly, the only place we used this path is in dump_node(),
143
 
     ### which immediately converts it into a relpath.  -- cmpilato.  */
144
191
  if (pb)
145
 
    abspath = svn_fspath__canonicalize(path, pool);
 
192
    repos_relpath = svn_relpath_canonicalize(path, pool);
146
193
  else
147
 
    abspath = "/";
 
194
    repos_relpath = "";
148
195
 
149
196
  /* Strip leading slash from copyfrom_path so that the path is
150
197
     canonical and svn_relpath_join can be used */
153
200
 
154
201
  new_db->eb = eb;
155
202
  new_db->parent_dir_baton = pb;
156
 
  new_db->abspath = abspath;
157
 
  new_db->copyfrom_path = copyfrom_path;
 
203
  new_db->pool = pool;
 
204
  new_db->repos_relpath = repos_relpath;
 
205
  new_db->copyfrom_path = copyfrom_path
 
206
                            ? svn_relpath_canonicalize(copyfrom_path, pool)
 
207
                            : NULL;
158
208
  new_db->copyfrom_rev = copyfrom_rev;
159
209
  new_db->added = added;
160
210
  new_db->written_out = FALSE;
 
211
  new_db->props = apr_hash_make(pool);
 
212
  new_db->deleted_props = apr_hash_make(pool);
161
213
  new_db->deleted_entries = apr_hash_make(pool);
162
214
 
163
215
  return new_db;
164
216
}
165
217
 
166
 
/* Extract and dump properties stored in edit baton EB, using POOL for
167
 
 * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
168
 
 * Unless DUMP_DATA_TOO is set, only property headers are dumped.
169
 
 */
 
218
/* Make a file baton to represent the directory at PATH (relative to
 
219
 * PB->eb).  PB is the directory baton of this directory's parent, or
 
220
 * NULL if this is the top-level directory of the edit.  Perform all
 
221
 * allocations in POOL.  */
 
222
static struct file_baton *
 
223
make_file_baton(const char *path,
 
224
                struct dir_baton *pb,
 
225
                apr_pool_t *pool)
 
226
{
 
227
  struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb));
 
228
 
 
229
  new_fb->eb = pb->eb;
 
230
  new_fb->parent_dir_baton = pb;
 
231
  new_fb->pool = pool;
 
232
  new_fb->repos_relpath = svn_relpath_canonicalize(path, pool);
 
233
  new_fb->props = apr_hash_make(pool);
 
234
  new_fb->deleted_props = apr_hash_make(pool);
 
235
  new_fb->is_copy = FALSE;
 
236
  new_fb->copyfrom_path = NULL;
 
237
  new_fb->copyfrom_rev = SVN_INVALID_REVNUM;
 
238
  new_fb->action = svn_node_action_change;
 
239
 
 
240
  return new_fb;
 
241
}
 
242
 
 
243
/* Return in *HEADER and *CONTENT the headers and content for PROPS. */
170
244
static svn_error_t *
171
 
do_dump_props(struct dump_edit_baton *eb,
172
 
              svn_boolean_t *trigger_var,
173
 
              svn_boolean_t dump_data_too,
174
 
              apr_pool_t *pool)
 
245
get_props_content(svn_stringbuf_t **header,
 
246
                  svn_stringbuf_t **content,
 
247
                  apr_hash_t *props,
 
248
                  apr_hash_t *deleted_props,
 
249
                  apr_pool_t *result_pool,
 
250
                  apr_pool_t *scratch_pool)
175
251
{
176
 
  svn_stream_t *propstream;
 
252
  svn_stream_t *content_stream;
177
253
  apr_hash_t *normal_props;
178
 
 
179
 
  if (trigger_var && !*trigger_var)
180
 
    return SVN_NO_ERROR;
181
 
 
182
 
  SVN_ERR(svn_rdump__normalize_props(&normal_props, eb->props, eb->pool));
183
 
  svn_stringbuf_setempty(eb->propstring);
184
 
  propstream = svn_stream_from_stringbuf(eb->propstring, eb->pool);
185
 
  SVN_ERR(svn_hash_write_incremental(normal_props, eb->deleted_props,
186
 
                                     propstream, "PROPS-END", pool));
187
 
  SVN_ERR(svn_stream_close(propstream));
 
254
  const char *buf;
 
255
 
 
256
  *content = svn_stringbuf_create_empty(result_pool);
 
257
  *header = svn_stringbuf_create_empty(result_pool);
 
258
 
 
259
  content_stream = svn_stream_from_stringbuf(*content, scratch_pool);
 
260
 
 
261
  SVN_ERR(svn_rdump__normalize_props(&normal_props, props, scratch_pool));
 
262
  SVN_ERR(svn_hash_write_incremental(normal_props, deleted_props,
 
263
                                     content_stream, "PROPS-END",
 
264
                                     scratch_pool));
 
265
  SVN_ERR(svn_stream_close(content_stream));
188
266
 
189
267
  /* Prop-delta: true */
190
 
  SVN_ERR(svn_stream_printf(eb->stream, pool,
191
 
                            SVN_REPOS_DUMPFILE_PROP_DELTA
192
 
                            ": true\n"));
 
268
  *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA
 
269
                                  ": true\n");
193
270
 
194
271
  /* Prop-content-length: 193 */
195
 
  SVN_ERR(svn_stream_printf(eb->stream, pool,
196
 
                            SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
197
 
                            ": %" APR_SIZE_T_FMT "\n", eb->propstring->len));
198
 
 
199
 
  if (dump_data_too)
 
272
  buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
 
273
                     ": %" APR_SIZE_T_FMT "\n", (*content)->len);
 
274
  svn_stringbuf_appendcstr(*header, buf);
 
275
 
 
276
  return SVN_NO_ERROR;
 
277
}
 
278
 
 
279
/* Extract and dump properties stored in PROPS and property deletions
 
280
 * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to
 
281
 * FALSE.
 
282
 *
 
283
 * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing
 
284
 * the content block of the property changes; otherwise, dump that to
 
285
 * the stream, too.
 
286
 */
 
287
static svn_error_t *
 
288
do_dump_props(svn_stringbuf_t **propstring,
 
289
              svn_stream_t *stream,
 
290
              apr_hash_t *props,
 
291
              apr_hash_t *deleted_props,
 
292
              svn_boolean_t *trigger_var,
 
293
              apr_pool_t *result_pool,
 
294
              apr_pool_t *scratch_pool)
 
295
{
 
296
  svn_stringbuf_t *header;
 
297
  svn_stringbuf_t *content;
 
298
  apr_size_t len;
 
299
 
 
300
  if (trigger_var && !*trigger_var)
 
301
    return SVN_NO_ERROR;
 
302
 
 
303
  SVN_ERR(get_props_content(&header, &content, props, deleted_props,
 
304
                            result_pool, scratch_pool));
 
305
  len = header->len;
 
306
  SVN_ERR(svn_stream_write(stream, header->data, &len));
 
307
 
 
308
  if (propstring)
 
309
    {
 
310
      *propstring = content;
 
311
    }
 
312
  else
200
313
    {
201
314
      /* Content-length: 14 */
202
 
      SVN_ERR(svn_stream_printf(eb->stream, pool,
 
315
      SVN_ERR(svn_stream_printf(stream, scratch_pool,
203
316
                                SVN_REPOS_DUMPFILE_CONTENT_LENGTH
204
317
                                ": %" APR_SIZE_T_FMT "\n\n",
205
 
                                eb->propstring->len));
 
318
                                content->len));
206
319
 
207
 
      /* The properties. */
208
 
      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
209
 
                               &(eb->propstring->len)));
 
320
      len = content->len;
 
321
      SVN_ERR(svn_stream_write(stream, content->data, &len));
210
322
 
211
323
      /* No text is going to be dumped. Write a couple of newlines and
212
324
         wait for the next node/ revision. */
213
 
      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
 
325
      SVN_ERR(svn_stream_puts(stream, "\n\n"));
214
326
 
215
327
      /* Cleanup so that data is never dumped twice. */
216
 
      SVN_ERR(svn_hash__clear(eb->props, eb->pool));
217
 
      SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
 
328
      apr_hash_clear(props);
 
329
      apr_hash_clear(deleted_props);
218
330
      if (trigger_var)
219
331
        *trigger_var = FALSE;
220
332
    }
229
341
{
230
342
  if (trigger_var && *trigger_var)
231
343
    {
232
 
      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
 
344
      SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
233
345
      *trigger_var = FALSE;
234
346
    }
235
347
  return SVN_NO_ERROR;
248
360
 */
249
361
static svn_error_t *
250
362
dump_node(struct dump_edit_baton *eb,
251
 
          const char *path,    /* an absolute path. */
252
 
          svn_node_kind_t kind,
 
363
          const char *repos_relpath,
 
364
          struct dir_baton *db,
 
365
          struct file_baton *fb,
253
366
          enum svn_node_action action,
254
367
          svn_boolean_t is_copy,
255
368
          const char *copyfrom_path,
256
369
          svn_revnum_t copyfrom_rev,
257
370
          apr_pool_t *pool)
258
371
{
259
 
  /* Remove leading slashes from path and copyfrom_path */
260
 
  if (path)
261
 
    path = svn_relpath_canonicalize(path, pool);
262
 
 
263
 
  if (copyfrom_path)
264
 
    copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool);
265
 
 
266
 
  /* Node-path: commons/STATUS */
 
372
  const char *node_relpath = repos_relpath;
 
373
 
 
374
  assert(svn_relpath_is_canonical(repos_relpath));
 
375
  assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path));
 
376
  assert(! (db && fb));
 
377
 
 
378
  /* Add the edit root relpath prefix if necessary. */
 
379
  if (eb->update_anchor_relpath)
 
380
    node_relpath = svn_relpath_join(eb->update_anchor_relpath,
 
381
                                    node_relpath, pool);
 
382
 
 
383
  /* Node-path: ... */
267
384
  SVN_ERR(svn_stream_printf(eb->stream, pool,
268
 
                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
 
385
                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
 
386
                            node_relpath));
269
387
 
270
 
  /* Node-kind: file */
271
 
  if (kind == svn_node_file)
 
388
  /* Node-kind: "file" | "dir" */
 
389
  if (fb)
272
390
    SVN_ERR(svn_stream_printf(eb->stream, pool,
273
391
                              SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
274
 
  else if (kind == svn_node_dir)
 
392
  else if (db)
275
393
    SVN_ERR(svn_stream_printf(eb->stream, pool,
276
394
                              SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
277
395
 
282
400
    case svn_node_action_change:
283
401
      /* We are here after a change_file_prop or change_dir_prop. They
284
402
         set up whatever dump_props they needed to- nothing to
285
 
         do here but print node action information */
286
 
      SVN_ERR(svn_stream_printf(eb->stream, pool,
287
 
                                SVN_REPOS_DUMPFILE_NODE_ACTION
288
 
                                ": change\n"));
 
403
         do here but print node action information.
 
404
 
 
405
         Node-action: change.  */
 
406
      SVN_ERR(svn_stream_puts(eb->stream,
 
407
                              SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n"));
289
408
      break;
290
409
 
291
410
    case svn_node_action_replace:
292
 
      if (!is_copy)
 
411
      if (is_copy)
 
412
        {
 
413
          /* Delete the original, and then re-add the replacement as a
 
414
             copy using recursive calls into this function. */
 
415
          SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete,
 
416
                            FALSE, NULL, SVN_INVALID_REVNUM, pool));
 
417
          SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add,
 
418
                            is_copy, copyfrom_path, copyfrom_rev, pool));
 
419
        }
 
420
      else
293
421
        {
294
422
          /* Node-action: replace */
295
 
          SVN_ERR(svn_stream_printf(eb->stream, pool,
296
 
                                    SVN_REPOS_DUMPFILE_NODE_ACTION
297
 
                                    ": replace\n"));
 
423
          SVN_ERR(svn_stream_puts(eb->stream,
 
424
                                  SVN_REPOS_DUMPFILE_NODE_ACTION
 
425
                                  ": replace\n"));
298
426
 
299
427
          /* Wait for a change_*_prop to be called before dumping
300
428
             anything */
301
 
          eb->dump_props = TRUE;
302
 
          break;
 
429
          if (fb)
 
430
            fb->dump_props = TRUE;
 
431
          else if (db)
 
432
            db->dump_props = TRUE;
303
433
        }
304
 
      /* More complex case: is_copy is true, and copyfrom_path/
305
 
         copyfrom_rev are present: delete the original, and then re-add
306
 
         it */
307
 
 
308
 
      SVN_ERR(svn_stream_printf(eb->stream, pool,
309
 
                                SVN_REPOS_DUMPFILE_NODE_ACTION
310
 
                                ": delete\n\n"));
311
 
 
312
 
      /* Recurse: Print an additional add-with-history record. */
313
 
      SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
314
 
                        is_copy, copyfrom_path, copyfrom_rev, pool));
315
 
 
316
 
      /* We can leave this routine quietly now, don't need to dump any
317
 
         content; that was already done in the second record. */
318
434
      break;
319
435
 
320
436
    case svn_node_action_delete:
321
 
      SVN_ERR(svn_stream_printf(eb->stream, pool,
322
 
                                SVN_REPOS_DUMPFILE_NODE_ACTION
323
 
                                ": delete\n"));
 
437
      /* Node-action: delete */
 
438
      SVN_ERR(svn_stream_puts(eb->stream,
 
439
                              SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n"));
324
440
 
325
441
      /* We can leave this routine quietly now. Nothing more to do-
326
442
         print a couple of newlines because we're not dumping props or
327
443
         text. */
328
 
      SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
 
444
      SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
 
445
 
329
446
      break;
330
447
 
331
448
    case svn_node_action_add:
332
 
      SVN_ERR(svn_stream_printf(eb->stream, pool,
333
 
                                SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
334
 
 
335
 
      if (!is_copy)
336
 
        {
337
 
          /* eb->dump_props for files is handled in close_file
338
 
             which is called immediately.  However, directories are not
339
 
             closed until all the work inside them has been done;
340
 
             eb->dump_props for directories is handled in all the
341
 
             functions that can possibly be called after add_directory:
342
 
             add_directory, open_directory, delete_entry, close_directory,
343
 
             add_file, open_file. change_dir_prop is a special case. */
344
 
 
345
 
          /* Wait for a change_*_prop to be called before dumping
346
 
             anything */
347
 
          eb->dump_props = TRUE;
348
 
          break;
349
 
        }
350
 
 
351
 
      SVN_ERR(svn_stream_printf(eb->stream, pool,
352
 
                                SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
353
 
                                ": %ld\n"
354
 
                                SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
355
 
                                ": %s\n",
356
 
                                copyfrom_rev, copyfrom_path));
357
 
 
358
 
      /* Ugly hack: If a directory was copied from a previous
359
 
         revision, nothing like close_file() will be called to write two
360
 
         blank lines. If change_dir_prop() is called, props are dumped
361
 
         (along with the necessary PROPS-END\n\n and we're good. So
362
 
         set DUMP_NEWLINES here to print the newlines unless
363
 
         change_dir_prop() is called next otherwise the `svnadmin load`
364
 
         parser will fail.  */
365
 
      if (kind == svn_node_dir)
366
 
        eb->dump_newlines = TRUE;
 
449
      /* Node-action: add */
 
450
      SVN_ERR(svn_stream_puts(eb->stream,
 
451
                              SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
 
452
 
 
453
      if (is_copy)
 
454
        {
 
455
          /* Node-copyfrom-rev / Node-copyfrom-path */
 
456
          SVN_ERR(svn_stream_printf(eb->stream, pool,
 
457
                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
 
458
                                    ": %ld\n"
 
459
                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
 
460
                                    ": %s\n",
 
461
                                    copyfrom_rev, copyfrom_path));
 
462
 
 
463
          /* Ugly hack: If a directory was copied from a previous
 
464
             revision, nothing like close_file() will be called to write two
 
465
             blank lines. If change_dir_prop() is called, props are dumped
 
466
             (along with the necessary PROPS-END\n\n and we're good. So
 
467
             set DUMP_NEWLINES here to print the newlines unless
 
468
             change_dir_prop() is called next otherwise the `svnadmin load`
 
469
             parser will fail.  */
 
470
          if (db)
 
471
            db->dump_newlines = TRUE;
 
472
        }
 
473
      else
 
474
        {
 
475
          /* fb->dump_props (for files) is handled in close_file()
 
476
             which is called immediately.
 
477
 
 
478
             However, directories are not closed until all the work
 
479
             inside them has been done; db->dump_props (for directories)
 
480
             is handled (via dump_pending()) in all the functions that
 
481
             can possibly be called after add_directory():
 
482
 
 
483
               - add_directory()
 
484
               - open_directory()
 
485
               - delete_entry()
 
486
               - close_directory()
 
487
               - add_file()
 
488
               - open_file()
 
489
 
 
490
             change_dir_prop() is a special case. */
 
491
          if (fb)
 
492
            fb->dump_props = TRUE;
 
493
          else if (db)
 
494
            db->dump_props = TRUE;
 
495
        }
367
496
 
368
497
      break;
369
498
    }
371
500
}
372
501
 
373
502
static svn_error_t *
 
503
dump_mkdir(struct dump_edit_baton *eb,
 
504
           const char *repos_relpath,
 
505
           apr_pool_t *pool)
 
506
{
 
507
  svn_stringbuf_t *prop_header, *prop_content;
 
508
  apr_size_t len;
 
509
  const char *buf;
 
510
 
 
511
  /* Node-path: ... */
 
512
  SVN_ERR(svn_stream_printf(eb->stream, pool,
 
513
                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
 
514
                            repos_relpath));
 
515
 
 
516
  /* Node-kind: dir */
 
517
  SVN_ERR(svn_stream_printf(eb->stream, pool,
 
518
                            SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
 
519
 
 
520
  /* Node-action: add */
 
521
  SVN_ERR(svn_stream_puts(eb->stream,
 
522
                          SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
 
523
 
 
524
  /* Dump the (empty) property block. */
 
525
  SVN_ERR(get_props_content(&prop_header, &prop_content,
 
526
                            apr_hash_make(pool), apr_hash_make(pool),
 
527
                            pool, pool));
 
528
  len = prop_header->len;
 
529
  SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len));
 
530
  len = prop_content->len;
 
531
  buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH
 
532
                     ": %" APR_SIZE_T_FMT "\n", len);
 
533
  SVN_ERR(svn_stream_puts(eb->stream, buf));
 
534
  SVN_ERR(svn_stream_puts(eb->stream, "\n"));
 
535
  SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len));
 
536
 
 
537
  /* Newlines to tie it all off. */
 
538
  SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
 
539
 
 
540
  return SVN_NO_ERROR;
 
541
}
 
542
 
 
543
/* Dump pending items from the specified node, to allow starting the dump
 
544
   of a child node */
 
545
static svn_error_t *
 
546
dump_pending(struct dump_edit_baton *eb,
 
547
             apr_pool_t *scratch_pool)
 
548
{
 
549
  if (! eb->pending_baton)
 
550
    return SVN_NO_ERROR;
 
551
 
 
552
  if (eb->pending_kind == svn_node_dir)
 
553
    {
 
554
      struct dir_baton *db = eb->pending_baton;
 
555
 
 
556
      /* Some pending properties to dump? */
 
557
      SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props,
 
558
                            &(db->dump_props), db->pool, scratch_pool));
 
559
 
 
560
      /* Some pending newlines to dump? */
 
561
      SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool));
 
562
    }
 
563
  else if (eb->pending_kind == svn_node_file)
 
564
    {
 
565
      struct file_baton *fb = eb->pending_baton;
 
566
 
 
567
      /* Some pending properties to dump? */
 
568
      SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props,
 
569
                            &(fb->dump_props), fb->pool, scratch_pool));
 
570
    }
 
571
  else
 
572
    abort();
 
573
 
 
574
  /* Anything that was pending is pending no longer. */
 
575
  eb->pending_baton = NULL;
 
576
  eb->pending_kind = svn_node_none;
 
577
 
 
578
  return SVN_NO_ERROR;
 
579
}
 
580
 
 
581
 
 
582
 
 
583
/*** Editor Function Implementations ***/
 
584
 
 
585
static svn_error_t *
374
586
open_root(void *edit_baton,
375
587
          svn_revnum_t base_revision,
376
588
          apr_pool_t *pool,
377
589
          void **root_baton)
378
590
{
379
591
  struct dump_edit_baton *eb = edit_baton;
 
592
  struct dir_baton *new_db = NULL;
380
593
 
381
594
  /* Clear the per-revision pool after each revision */
382
595
  svn_pool_clear(eb->pool);
383
596
 
384
 
  eb->props = apr_hash_make(eb->pool);
385
 
  eb->deleted_props = apr_hash_make(eb->pool);
386
 
  eb->propstring = svn_stringbuf_create("", eb->pool);
387
 
 
388
 
  *root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
389
 
                               edit_baton, NULL, FALSE, eb->pool);
390
597
  LDR_DBG(("open_root %p\n", *root_baton));
391
598
 
 
599
  if (eb->update_anchor_relpath)
 
600
    {
 
601
      int i;
 
602
      const char *parent_path = eb->update_anchor_relpath;
 
603
      apr_array_header_t *dirs_to_add =
 
604
        apr_array_make(pool, 4, sizeof(const char *));
 
605
      apr_pool_t *iterpool = svn_pool_create(pool);
 
606
 
 
607
      while (! svn_path_is_empty(parent_path))
 
608
        {
 
609
          APR_ARRAY_PUSH(dirs_to_add, const char *) = parent_path;
 
610
          parent_path = svn_relpath_dirname(parent_path, pool);
 
611
        }
 
612
 
 
613
      for (i = dirs_to_add->nelts; i; --i)
 
614
        {
 
615
          const char *dir_to_add =
 
616
            APR_ARRAY_IDX(dirs_to_add, i - 1, const char *);
 
617
 
 
618
          svn_pool_clear(iterpool);
 
619
 
 
620
          /* For parents of the source directory, we just manufacture
 
621
             the adds ourselves. */
 
622
          if (i > 1)
 
623
            {
 
624
              SVN_ERR(dump_mkdir(eb, dir_to_add, iterpool));
 
625
            }
 
626
          else
 
627
            {
 
628
              /* ... but for the source directory itself, we'll defer
 
629
                 to letting the typical plumbing handle this task. */
 
630
              new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
 
631
                                      edit_baton, NULL, TRUE, pool);
 
632
              SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db,
 
633
                                NULL, svn_node_action_add, FALSE,
 
634
                                NULL, SVN_INVALID_REVNUM, pool));
 
635
 
 
636
              /* Remember that we've started but not yet finished
 
637
                 handling this directory. */
 
638
              new_db->written_out = TRUE;
 
639
              eb->pending_baton = new_db;
 
640
              eb->pending_kind = svn_node_dir;
 
641
            }
 
642
        }
 
643
      svn_pool_destroy(iterpool);
 
644
    }
 
645
 
 
646
  if (! new_db)
 
647
    {
 
648
      new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
 
649
                              edit_baton, NULL, FALSE, pool);
 
650
    }
 
651
 
 
652
  *root_baton = new_db;
392
653
  return SVN_NO_ERROR;
393
654
}
394
655
 
402
663
 
403
664
  LDR_DBG(("delete_entry %s\n", path));
404
665
 
405
 
  /* Some pending properties to dump? */
406
 
  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
407
 
 
408
 
  /* Some pending newlines to dump? */
409
 
  SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
410
 
 
411
 
  /* Add this path to the deleted_entries of the parent directory
412
 
     baton. */
413
 
  apr_hash_set(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path),
414
 
               APR_HASH_KEY_STRING, pb);
 
666
  SVN_ERR(dump_pending(pb->eb, pool));
 
667
 
 
668
  /* We don't dump this deletion immediate.  Rather, we add this path
 
669
     to the deleted_entries of the parent directory baton.  That way,
 
670
     we can tell (later) an addition from a replacement.  All the real
 
671
     deletions get handled in close_directory().  */
 
672
  svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path), pb);
415
673
 
416
674
  return SVN_NO_ERROR;
417
675
}
431
689
 
432
690
  LDR_DBG(("add_directory %s\n", path));
433
691
 
 
692
  SVN_ERR(dump_pending(pb->eb, pool));
 
693
 
434
694
  new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb,
435
695
                          pb, TRUE, pb->eb->pool);
436
696
 
437
 
  /* Some pending properties to dump? */
438
 
  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
439
 
 
440
 
  /* Some pending newlines to dump? */
441
 
  SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
442
 
 
443
697
  /* This might be a replacement -- is the path already deleted? */
444
 
  val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
698
  val = svn_hash_gets(pb->deleted_entries, path);
445
699
 
446
700
  /* Detect an add-with-history */
447
701
  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
448
702
 
449
703
  /* Dump the node */
450
 
  SVN_ERR(dump_node(pb->eb, path,
451
 
                    svn_node_dir,
 
704
  SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL,
452
705
                    val ? svn_node_action_replace : svn_node_action_add,
453
706
                    is_copy,
454
 
                    is_copy ? copyfrom_path : NULL,
 
707
                    is_copy ? new_db->copyfrom_path : NULL,
455
708
                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
456
709
                    pool));
457
710
 
458
711
  if (val)
459
712
    /* Delete the path, it's now been dumped */
460
 
    apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
 
713
    svn_hash_sets(pb->deleted_entries, path, NULL);
461
714
 
 
715
  /* Remember that we've started, but not yet finished handling this
 
716
     directory. */
462
717
  new_db->written_out = TRUE;
 
718
  pb->eb->pending_baton = new_db;
 
719
  pb->eb->pending_kind = svn_node_dir;
463
720
 
464
721
  *child_baton = new_db;
465
722
  return SVN_NO_ERROR;
479
736
 
480
737
  LDR_DBG(("open_directory %s\n", path));
481
738
 
482
 
  /* Some pending properties to dump? */
483
 
  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
484
 
 
485
 
  /* Some pending newlines to dump? */
486
 
  SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
 
739
  SVN_ERR(dump_pending(pb->eb, pool));
487
740
 
488
741
  /* If the parent directory has explicit comparison path and rev,
489
742
     record the same for this one. */
497
750
 
498
751
  new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
499
752
                          FALSE, pb->eb->pool);
 
753
 
500
754
  *child_baton = new_db;
501
755
  return SVN_NO_ERROR;
502
756
}
506
760
                apr_pool_t *pool)
507
761
{
508
762
  struct dir_baton *db = dir_baton;
509
 
  struct dump_edit_baton *eb = db->eb;
510
763
  apr_hash_index_t *hi;
 
764
  svn_boolean_t this_pending;
511
765
 
512
766
  LDR_DBG(("close_directory %p\n", dir_baton));
513
767
 
514
 
  /* Some pending properties to dump? */
515
 
  SVN_ERR(do_dump_props(eb, &(eb->dump_props), TRUE, pool));
516
 
 
517
 
  /* Some pending newlines to dump? */
518
 
  SVN_ERR(do_dump_newlines(eb, &(eb->dump_newlines), pool));
 
768
  /* Remember if this directory is the one currently pending. */
 
769
  this_pending = (db->eb->pending_baton == db);
 
770
 
 
771
  SVN_ERR(dump_pending(db->eb, pool));
 
772
 
 
773
  /* If this directory was pending, then dump_pending() should have
 
774
     taken care of all the props and such.  Of course, the only way
 
775
     that would be the case is if this directory was added/replaced.
 
776
 
 
777
     Otherwise, if stuff for this directory has already been written
 
778
     out (at some point in the past, prior to our handling other
 
779
     nodes), we might need to generate a second "change" record just
 
780
     to carry the information we've since learned about the
 
781
     directory. */
 
782
  if ((! this_pending) && (db->dump_props))
 
783
    {
 
784
      SVN_ERR(dump_node(db->eb, db->repos_relpath, db, NULL,
 
785
                        svn_node_action_change, FALSE,
 
786
                        NULL, SVN_INVALID_REVNUM, pool));
 
787
      db->eb->pending_baton = db;
 
788
      db->eb->pending_kind = svn_node_dir;
 
789
      SVN_ERR(dump_pending(db->eb, pool));
 
790
    }
519
791
 
520
792
  /* Dump the deleted directory entries */
521
793
  for (hi = apr_hash_first(pool, db->deleted_entries); hi;
523
795
    {
524
796
      const char *path = svn__apr_hash_index_key(hi);
525
797
 
526
 
      SVN_ERR(dump_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
 
798
      SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete,
527
799
                        FALSE, NULL, SVN_INVALID_REVNUM, pool));
528
800
    }
529
801
 
530
 
  SVN_ERR(svn_hash__clear(db->deleted_entries, pool));
 
802
  /* ### should be unnecessary */
 
803
  apr_hash_clear(db->deleted_entries);
 
804
 
531
805
  return SVN_NO_ERROR;
532
806
}
533
807
 
540
814
         void **file_baton)
541
815
{
542
816
  struct dir_baton *pb = parent_baton;
 
817
  struct file_baton *fb;
543
818
  void *val;
544
 
  svn_boolean_t is_copy;
545
819
 
546
820
  LDR_DBG(("add_file %s\n", path));
547
821
 
548
 
  /* Some pending properties to dump? */
549
 
  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
 
822
  SVN_ERR(dump_pending(pb->eb, pool));
550
823
 
551
 
  /* Some pending newlines to dump? */
552
 
  SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
 
824
  /* Make the file baton. */
 
825
  fb = make_file_baton(path, pb, pool);
553
826
 
554
827
  /* This might be a replacement -- is the path already deleted? */
555
 
  val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
828
  val = svn_hash_gets(pb->deleted_entries, path);
556
829
 
557
830
  /* Detect add-with-history. */
558
 
  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
559
 
 
560
 
  /* Dump the node. */
561
 
  SVN_ERR(dump_node(pb->eb, path,
562
 
                    svn_node_file,
563
 
                    val ? svn_node_action_replace : svn_node_action_add,
564
 
                    is_copy,
565
 
                    is_copy ? copyfrom_path : NULL,
566
 
                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
567
 
                    pool));
568
 
 
 
831
  if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev))
 
832
    {
 
833
      fb->copyfrom_path = svn_relpath_canonicalize(copyfrom_path, fb->pool);
 
834
      fb->copyfrom_rev = copyfrom_rev;
 
835
      fb->is_copy = TRUE;
 
836
    }
 
837
  fb->action = val ? svn_node_action_replace : svn_node_action_add;
 
838
 
 
839
  /* Delete the path, it's now been dumped. */
569
840
  if (val)
570
 
    /* delete the path, it's now been dumped. */
571
 
    apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
572
 
 
573
 
  /* Build a nice file baton to pass to change_file_prop and
574
 
     apply_textdelta */
575
 
  *file_baton = pb->eb;
576
 
 
 
841
    svn_hash_sets(pb->deleted_entries, path, NULL);
 
842
 
 
843
  *file_baton = fb;
577
844
  return SVN_NO_ERROR;
578
845
}
579
846
 
585
852
          void **file_baton)
586
853
{
587
854
  struct dir_baton *pb = parent_baton;
588
 
  const char *copyfrom_path = NULL;
589
 
  svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
 
855
  struct file_baton *fb;
590
856
 
591
857
  LDR_DBG(("open_file %s\n", path));
592
858
 
593
 
  /* Some pending properties to dump? */
594
 
  SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
 
859
  SVN_ERR(dump_pending(pb->eb, pool));
595
860
 
596
 
  /* Some pending newlines to dump? */
597
 
  SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
 
861
  /* Make the file baton. */
 
862
  fb = make_file_baton(path, pb, pool);
598
863
 
599
864
  /* If the parent directory has explicit copyfrom path and rev,
600
865
     record the same for this one. */
601
866
  if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
602
867
    {
603
 
      copyfrom_path = svn_relpath_join(pb->copyfrom_path,
604
 
                                       svn_relpath_basename(path, NULL),
605
 
                                       pb->eb->pool);
606
 
      copyfrom_rev = pb->copyfrom_rev;
 
868
      fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path,
 
869
                                           svn_relpath_basename(path, NULL),
 
870
                                           pb->eb->pool);
 
871
      fb->copyfrom_rev = pb->copyfrom_rev;
607
872
    }
608
873
 
609
 
  SVN_ERR(dump_node(pb->eb, path, svn_node_file, svn_node_action_change,
610
 
                    FALSE, copyfrom_path, copyfrom_rev, pool));
611
 
 
612
 
  /* Build a nice file baton to pass to change_file_prop and
613
 
     apply_textdelta */
614
 
  *file_baton = pb->eb;
615
 
 
 
874
  *file_baton = fb;
616
875
  return SVN_NO_ERROR;
617
876
}
618
877
 
623
882
                apr_pool_t *pool)
624
883
{
625
884
  struct dir_baton *db = parent_baton;
 
885
  svn_boolean_t this_pending;
626
886
 
627
887
  LDR_DBG(("change_dir_prop %p\n", parent_baton));
628
888
 
629
 
  if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
 
889
  /* This directory is not pending, but something else is, so handle
 
890
     the "something else".  */
 
891
  this_pending = (db->eb->pending_baton == db);
 
892
  if (! this_pending)
 
893
    SVN_ERR(dump_pending(db->eb, pool));
 
894
 
 
895
  if (svn_property_kind2(name) != svn_prop_regular_kind)
630
896
    return SVN_NO_ERROR;
631
897
 
632
898
  if (value)
633
 
    apr_hash_set(db->eb->props, apr_pstrdup(db->eb->pool, name),
634
 
                 APR_HASH_KEY_STRING, svn_string_dup(value, db->eb->pool));
 
899
    svn_hash_sets(db->props,
 
900
                  apr_pstrdup(db->pool, name),
 
901
                  svn_string_dup(value, db->pool));
635
902
  else
636
 
    apr_hash_set(db->eb->deleted_props, apr_pstrdup(db->eb->pool, name),
637
 
                 APR_HASH_KEY_STRING, "");
638
 
 
639
 
  if (! db->written_out)
640
 
    {
641
 
      /* If db->written_out is set, it means that the node information
642
 
         corresponding to this directory has already been written: don't
643
 
         do anything; do_dump_props() will take care of dumping the
644
 
         props. If it not, dump the node itself before dumping the
645
 
         props. */
646
 
 
647
 
      SVN_ERR(dump_node(db->eb, db->abspath, svn_node_dir,
648
 
                        svn_node_action_change, FALSE, db->copyfrom_path,
649
 
                        db->copyfrom_rev, pool));
650
 
      db->written_out = TRUE;
651
 
    }
 
903
    svn_hash_sets(db->deleted_props, apr_pstrdup(db->pool, name), "");
652
904
 
653
905
  /* Make sure we eventually output the props, and disable printing
654
906
     a couple of extra newlines */
655
 
  db->eb->dump_newlines = FALSE;
656
 
  db->eb->dump_props = TRUE;
 
907
  db->dump_newlines = FALSE;
 
908
  db->dump_props = TRUE;
657
909
 
658
910
  return SVN_NO_ERROR;
659
911
}
664
916
                 const svn_string_t *value,
665
917
                 apr_pool_t *pool)
666
918
{
667
 
  struct dump_edit_baton *eb = file_baton;
 
919
  struct file_baton *fb = file_baton;
668
920
 
669
921
  LDR_DBG(("change_file_prop %p\n", file_baton));
670
922
 
671
 
  if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
 
923
  if (svn_property_kind2(name) != svn_prop_regular_kind)
672
924
    return SVN_NO_ERROR;
673
925
 
674
926
  if (value)
675
 
    apr_hash_set(eb->props, apr_pstrdup(eb->pool, name),
676
 
                 APR_HASH_KEY_STRING, svn_string_dup(value, eb->pool));
 
927
    svn_hash_sets(fb->props,
 
928
                  apr_pstrdup(fb->pool, name),
 
929
                  svn_string_dup(value, fb->pool));
677
930
  else
678
 
    apr_hash_set(eb->deleted_props, apr_pstrdup(eb->pool, name),
679
 
                 APR_HASH_KEY_STRING, "");
 
931
    svn_hash_sets(fb->deleted_props, apr_pstrdup(fb->pool, name), "");
680
932
 
681
933
  /* Dump the property headers and wait; close_file might need
682
934
     to write text headers too depending on whether
683
935
     apply_textdelta is called */
684
 
  eb->dump_props = TRUE;
 
936
  fb->dump_props = TRUE;
685
937
 
686
938
  return SVN_NO_ERROR;
687
939
}
708
960
                svn_txdelta_window_handler_t *handler,
709
961
                void **handler_baton)
710
962
{
711
 
  struct dump_edit_baton *eb = file_baton;
712
 
 
713
 
  /* Custom handler_baton allocated in a separate pool */
 
963
  struct file_baton *fb = file_baton;
 
964
  struct dump_edit_baton *eb = fb->eb;
714
965
  struct handler_baton *hb;
715
966
  svn_stream_t *delta_filestream;
716
967
 
 
968
  LDR_DBG(("apply_textdelta %p\n", file_baton));
 
969
 
 
970
  /* This is custom handler_baton, allocated from a separate pool.  */
717
971
  hb = apr_pcalloc(eb->pool, sizeof(*hb));
718
972
 
719
 
  LDR_DBG(("apply_textdelta %p\n", file_baton));
720
 
 
721
 
  /* Use a temporary file to measure the text-content-length */
 
973
  /* Use a temporary file to measure the Text-content-length */
722
974
  delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool);
723
975
 
724
976
  /* Prepare to write the delta to the delta_filestream */
726
978
                          delta_filestream, 0,
727
979
                          SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
728
980
 
729
 
  eb->dump_text = TRUE;
730
 
  eb->base_checksum = apr_pstrdup(eb->pool, base_checksum);
731
 
  SVN_ERR(svn_stream_close(delta_filestream));
 
981
  /* Record that there's text to be dumped, and its base checksum. */
 
982
  fb->dump_text = TRUE;
 
983
  fb->base_checksum = apr_pstrdup(eb->pool, base_checksum);
732
984
 
733
985
  /* The actual writing takes place when this function has
734
986
     finished. Set handler and handler_baton now so for
744
996
           const char *text_checksum,
745
997
           apr_pool_t *pool)
746
998
{
747
 
  struct dump_edit_baton *eb = file_baton;
 
999
  struct file_baton *fb = file_baton;
 
1000
  struct dump_edit_baton *eb = fb->eb;
748
1001
  apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
 
1002
  svn_stringbuf_t *propstring;
749
1003
 
750
1004
  LDR_DBG(("close_file %p\n", file_baton));
751
1005
 
752
 
  /* Some pending properties to dump? Dump just the headers- dump the
753
 
     props only after dumping the text headers too (if present) */
754
 
  SVN_ERR(do_dump_props(eb, &(eb->dump_props), FALSE, pool));
 
1006
  SVN_ERR(dump_pending(eb, pool));
 
1007
 
 
1008
  /* Dump the node. */
 
1009
  SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb,
 
1010
                    fb->action, fb->is_copy, fb->copyfrom_path,
 
1011
                    fb->copyfrom_rev, pool));
 
1012
 
 
1013
  /* Some pending properties to dump?  We'll dump just the headers for
 
1014
     now, then dump the actual propchange content only after dumping
 
1015
     the text headers too (if present). */
 
1016
  SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props,
 
1017
                        &(fb->dump_props), pool, pool));
755
1018
 
756
1019
  /* Dump the text headers */
757
 
  if (eb->dump_text)
 
1020
  if (fb->dump_text)
758
1021
    {
759
1022
      apr_status_t err;
760
1023
 
761
1024
      /* Text-delta: true */
762
 
      SVN_ERR(svn_stream_printf(eb->stream, pool,
763
 
                                SVN_REPOS_DUMPFILE_TEXT_DELTA
764
 
                                ": true\n"));
 
1025
      SVN_ERR(svn_stream_puts(eb->stream,
 
1026
                              SVN_REPOS_DUMPFILE_TEXT_DELTA
 
1027
                              ": true\n"));
765
1028
 
766
1029
      err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file);
767
1030
      if (err)
768
1031
        SVN_ERR(svn_error_wrap_apr(err, NULL));
769
1032
 
770
 
      if (eb->base_checksum)
 
1033
      if (fb->base_checksum)
771
1034
        /* Text-delta-base-md5: */
772
1035
        SVN_ERR(svn_stream_printf(eb->stream, pool,
773
1036
                                  SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
774
1037
                                  ": %s\n",
775
 
                                  eb->base_checksum));
 
1038
                                  fb->base_checksum));
776
1039
 
777
1040
      /* Text-content-length: 39 */
778
1041
      SVN_ERR(svn_stream_printf(eb->stream, pool,
789
1052
 
790
1053
  /* Content-length: 1549 */
791
1054
  /* If both text and props are absent, skip this header */
792
 
  if (eb->dump_props)
 
1055
  if (fb->dump_props)
793
1056
    SVN_ERR(svn_stream_printf(eb->stream, pool,
794
1057
                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
795
1058
                              ": %ld\n\n",
796
 
                              (unsigned long)info->size + eb->propstring->len));
797
 
  else if (eb->dump_text)
 
1059
                              (unsigned long)info->size + propstring->len));
 
1060
  else if (fb->dump_text)
798
1061
    SVN_ERR(svn_stream_printf(eb->stream, pool,
799
1062
                              SVN_REPOS_DUMPFILE_CONTENT_LENGTH
800
1063
                              ": %ld\n\n",
801
1064
                              (unsigned long)info->size));
802
1065
 
803
1066
  /* Dump the props now */
804
 
  if (eb->dump_props)
 
1067
  if (fb->dump_props)
805
1068
    {
806
 
      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
807
 
                               &(eb->propstring->len)));
 
1069
      SVN_ERR(svn_stream_write(eb->stream, propstring->data,
 
1070
                               &(propstring->len)));
808
1071
 
809
1072
      /* Cleanup */
810
 
      eb->dump_props = FALSE;
811
 
      SVN_ERR(svn_hash__clear(eb->props, eb->pool));
812
 
      SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
 
1073
      fb->dump_props = FALSE;
 
1074
      apr_hash_clear(fb->props);
 
1075
      apr_hash_clear(fb->deleted_props);
813
1076
    }
814
1077
 
815
1078
  /* Dump the text */
816
 
  if (eb->dump_text)
 
1079
  if (fb->dump_text)
817
1080
    {
818
1081
      /* Seek to the beginning of the delta file, map it to a stream,
819
1082
         and copy the stream to eb->stream. Then close the stream and
830
1093
      /* Cleanup */
831
1094
      SVN_ERR(svn_stream_close(delta_filestream));
832
1095
      SVN_ERR(svn_io_file_trunc(eb->delta_file, 0, pool));
833
 
      eb->dump_text = FALSE;
834
1096
    }
835
1097
 
836
1098
  /* Write a couple of blank lines for matching output with `svnadmin
837
1099
     dump` */
838
 
  SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
 
1100
  SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
839
1101
 
840
1102
  return SVN_NO_ERROR;
841
1103
}
846
1108
  return SVN_NO_ERROR;
847
1109
}
848
1110
 
 
1111
static svn_error_t *
 
1112
fetch_base_func(const char **filename,
 
1113
                void *baton,
 
1114
                const char *path,
 
1115
                svn_revnum_t base_revision,
 
1116
                apr_pool_t *result_pool,
 
1117
                apr_pool_t *scratch_pool)
 
1118
{
 
1119
  struct dump_edit_baton *eb = baton;
 
1120
  svn_stream_t *fstream;
 
1121
  svn_error_t *err;
 
1122
 
 
1123
  if (path[0] == '/')
 
1124
    path += 1;
 
1125
 
 
1126
  if (! SVN_IS_VALID_REVNUM(base_revision))
 
1127
    base_revision = eb->current_revision - 1;
 
1128
 
 
1129
  SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
 
1130
                                 svn_io_file_del_on_pool_cleanup,
 
1131
                                 result_pool, scratch_pool));
 
1132
 
 
1133
  err = svn_ra_get_file(eb->ra_session, path, base_revision,
 
1134
                        fstream, NULL, NULL, scratch_pool);
 
1135
  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
 
1136
    {
 
1137
      svn_error_clear(err);
 
1138
      SVN_ERR(svn_stream_close(fstream));
 
1139
 
 
1140
      *filename = NULL;
 
1141
      return SVN_NO_ERROR;
 
1142
    }
 
1143
  else if (err)
 
1144
    return svn_error_trace(err);
 
1145
 
 
1146
  SVN_ERR(svn_stream_close(fstream));
 
1147
 
 
1148
  return SVN_NO_ERROR;
 
1149
}
 
1150
 
 
1151
static svn_error_t *
 
1152
fetch_props_func(apr_hash_t **props,
 
1153
                 void *baton,
 
1154
                 const char *path,
 
1155
                 svn_revnum_t base_revision,
 
1156
                 apr_pool_t *result_pool,
 
1157
                 apr_pool_t *scratch_pool)
 
1158
{
 
1159
  struct dump_edit_baton *eb = baton;
 
1160
  svn_node_kind_t node_kind;
 
1161
 
 
1162
  if (path[0] == '/')
 
1163
    path += 1;
 
1164
 
 
1165
  if (! SVN_IS_VALID_REVNUM(base_revision))
 
1166
    base_revision = eb->current_revision - 1;
 
1167
 
 
1168
  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
 
1169
                            scratch_pool));
 
1170
 
 
1171
  if (node_kind == svn_node_file)
 
1172
    {
 
1173
      SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
 
1174
                              NULL, NULL, props, result_pool));
 
1175
    }
 
1176
  else if (node_kind == svn_node_dir)
 
1177
    {
 
1178
      apr_array_header_t *tmp_props;
 
1179
 
 
1180
      SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
 
1181
                              base_revision, 0 /* Dirent fields */,
 
1182
                              result_pool));
 
1183
      tmp_props = svn_prop_hash_to_array(*props, result_pool);
 
1184
      SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
 
1185
                                   result_pool));
 
1186
      *props = svn_prop_array_to_hash(tmp_props, result_pool);
 
1187
    }
 
1188
  else
 
1189
    {
 
1190
      *props = apr_hash_make(result_pool);
 
1191
    }
 
1192
 
 
1193
  return SVN_NO_ERROR;
 
1194
}
 
1195
 
 
1196
static svn_error_t *
 
1197
fetch_kind_func(svn_node_kind_t *kind,
 
1198
                void *baton,
 
1199
                const char *path,
 
1200
                svn_revnum_t base_revision,
 
1201
                apr_pool_t *scratch_pool)
 
1202
{
 
1203
  struct dump_edit_baton *eb = baton;
 
1204
 
 
1205
  if (path[0] == '/')
 
1206
    path += 1;
 
1207
 
 
1208
  if (! SVN_IS_VALID_REVNUM(base_revision))
 
1209
    base_revision = eb->current_revision - 1;
 
1210
 
 
1211
  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind,
 
1212
                            scratch_pool));
 
1213
 
 
1214
  return SVN_NO_ERROR;
 
1215
}
 
1216
 
849
1217
svn_error_t *
850
1218
svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
851
1219
                           void **edit_baton,
 
1220
                           svn_revnum_t revision,
852
1221
                           svn_stream_t *stream,
 
1222
                           svn_ra_session_t *ra_session,
 
1223
                           const char *update_anchor_relpath,
853
1224
                           svn_cancel_func_t cancel_func,
854
1225
                           void *cancel_baton,
855
1226
                           apr_pool_t *pool)
856
1227
{
857
1228
  struct dump_edit_baton *eb;
858
1229
  svn_delta_editor_t *de;
 
1230
  svn_delta_shim_callbacks_t *shim_callbacks =
 
1231
                                        svn_delta_shim_callbacks_default(pool);
859
1232
 
860
1233
  eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
861
1234
  eb->stream = stream;
 
1235
  eb->ra_session = ra_session;
 
1236
  eb->update_anchor_relpath = update_anchor_relpath;
 
1237
  eb->current_revision = revision;
 
1238
  eb->pending_kind = svn_node_none;
862
1239
 
863
1240
  /* Create a special per-revision pool */
864
1241
  eb->pool = svn_pool_create(pool);
888
1265
  *editor = de;
889
1266
 
890
1267
  /* Wrap this editor in a cancellation editor. */
891
 
  return svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
892
 
                                           de, eb, editor, edit_baton, pool);
 
1268
  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
 
1269
                                            de, eb, editor, edit_baton, pool));
 
1270
 
 
1271
  shim_callbacks->fetch_base_func = fetch_base_func;
 
1272
  shim_callbacks->fetch_props_func = fetch_props_func;
 
1273
  shim_callbacks->fetch_kind_func = fetch_kind_func;
 
1274
  shim_callbacks->fetch_baton = eb;
 
1275
 
 
1276
  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
 
1277
                                   NULL, NULL, shim_callbacks, pool, pool));
 
1278
 
 
1279
  return SVN_NO_ERROR;
893
1280
}