~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/export.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
 * export.c:  export a tree.
 
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
 
 
22
 
 
23
/*** Includes. ***/
 
24
 
 
25
#include <apr_file_io.h>
 
26
#include <apr_md5.h>
 
27
#include "svn_types.h"
 
28
#include "svn_wc.h"
 
29
#include "svn_client.h"
 
30
#include "svn_string.h"
 
31
#include "svn_error.h"
 
32
#include "svn_path.h"
 
33
#include "svn_pools.h"
 
34
#include "svn_subst.h"
 
35
#include "svn_time.h"
 
36
#include "svn_md5.h"
 
37
#include "svn_props.h"
 
38
#include "client.h"
 
39
 
 
40
#include "svn_private_config.h"
 
41
 
 
42
 
 
43
/*** Code. ***/
 
44
 
 
45
/* Add EXTERNALS_PROP_VAL for the export destination path PATH to
 
46
   TRAVERSAL_INFO.  */
 
47
static void
 
48
add_externals (apr_hash_t *externals,
 
49
               const char *path,
 
50
               const svn_string_t *externals_prop_val)
 
51
{
 
52
  apr_pool_t *pool = apr_hash_pool_get (externals);
 
53
 
 
54
  if (! externals_prop_val)
 
55
    return;
 
56
 
 
57
  apr_hash_set (externals, 
 
58
                apr_pstrdup (pool, path), 
 
59
                APR_HASH_KEY_STRING, 
 
60
                apr_pstrmemdup (pool, externals_prop_val->data,
 
61
                                externals_prop_val->len));
 
62
}
 
63
 
 
64
/* Helper function that gets the eol style and optionally overrides the
 
65
   EOL marker for files marked as native with the EOL marker matching
 
66
   the string specified in requested_value which is of the same format
 
67
   as the svn:eol-style property values. */
 
68
static svn_error_t *
 
69
get_eol_style (svn_subst_eol_style_t *style,
 
70
               const char **eol,
 
71
               const char *value,
 
72
               const char *requested_value)
 
73
{
 
74
  svn_subst_eol_style_from_value (style, eol, value);
 
75
  if (requested_value && *style == svn_subst_eol_style_native)
 
76
    {
 
77
      svn_subst_eol_style_t requested_style;
 
78
      const char *requested_eol;
 
79
      
 
80
      svn_subst_eol_style_from_value (&requested_style, &requested_eol,
 
81
                                      requested_value);
 
82
 
 
83
      if (requested_style == svn_subst_eol_style_fixed)
 
84
        *eol = requested_eol;
 
85
      else
 
86
        return svn_error_createf (SVN_ERR_IO_UNKNOWN_EOL, NULL,
 
87
                                  _("'%s' is not a valid EOL value"),
 
88
                                  requested_value);
 
89
    }
 
90
  return SVN_NO_ERROR;
 
91
}
 
92
 
 
93
static svn_error_t *
 
94
copy_one_versioned_file (const char *from,
 
95
                         const char *to,
 
96
                         svn_wc_adm_access_t *adm_access,
 
97
                         svn_opt_revision_t *revision,
 
98
                         const char *native_eol,
 
99
                         apr_pool_t *pool)
 
100
{
 
101
  const svn_wc_entry_t *entry;
 
102
  svn_subst_keywords_t kw = { 0 };
 
103
  svn_subst_eol_style_t style;
 
104
  apr_hash_t *props;
 
105
  const char *base;
 
106
  svn_string_t *eol_style, *keywords, *executable, *externals, *special;
 
107
  const char *eol = NULL;
 
108
  svn_boolean_t local_mod = FALSE;
 
109
  apr_time_t tm;
 
110
          
 
111
  SVN_ERR (svn_wc_entry (&entry, from, adm_access, FALSE, pool));
 
112
 
 
113
  /* Only export 'added' files when the revision is WORKING.
 
114
     Otherwise, skip the 'added' files, since they didn't exist
 
115
     in the BASE revision and don't have an associated text-base.
 
116
 
 
117
     Don't export 'deleted' files and directories unless it's a
 
118
     revision other than WORKING.  These files and directories
 
119
     don't really exists in WORKING. */
 
120
  if ((revision->kind != svn_opt_revision_working &&
 
121
       entry->schedule == svn_wc_schedule_add) ||
 
122
      (revision->kind == svn_opt_revision_working &&
 
123
       entry->schedule == svn_wc_schedule_delete))
 
124
    return SVN_NO_ERROR;
 
125
 
 
126
  if (revision->kind != svn_opt_revision_working)
 
127
    {
 
128
      SVN_ERR (svn_wc_get_pristine_copy_path (from, &base, 
 
129
                                              pool));
 
130
      SVN_ERR (svn_wc_get_prop_diffs (NULL, &props, from, 
 
131
                                      adm_access, pool));
 
132
    }
 
133
  else
 
134
    {
 
135
      svn_wc_status2_t *status;
 
136
      
 
137
      base = from;
 
138
      SVN_ERR (svn_wc_prop_list (&props, from, 
 
139
                                 adm_access, pool));
 
140
      SVN_ERR (svn_wc_status2 (&status, from, 
 
141
                               adm_access, pool));
 
142
      if (status->text_status != svn_wc_status_normal)
 
143
        local_mod = TRUE;
 
144
    }
 
145
  
 
146
  eol_style = apr_hash_get (props, SVN_PROP_EOL_STYLE,
 
147
                            APR_HASH_KEY_STRING);
 
148
  keywords = apr_hash_get (props, SVN_PROP_KEYWORDS,
 
149
                           APR_HASH_KEY_STRING);
 
150
  executable = apr_hash_get (props, SVN_PROP_EXECUTABLE,
 
151
                             APR_HASH_KEY_STRING);
 
152
  externals = apr_hash_get (props, SVN_PROP_EXTERNALS,
 
153
                            APR_HASH_KEY_STRING);
 
154
  special = apr_hash_get (props, SVN_PROP_SPECIAL,
 
155
                          APR_HASH_KEY_STRING);
 
156
  
 
157
  if (eol_style)
 
158
    SVN_ERR (get_eol_style (&style, &eol, eol_style->data, native_eol));
 
159
  
 
160
  if (local_mod && (! special))
 
161
    {
 
162
      /* Use the modified time from the working copy if
 
163
         the file */
 
164
      SVN_ERR (svn_io_file_affected_time (&tm, from, pool));
 
165
    }
 
166
  else
 
167
    {
 
168
      tm = entry->cmt_date;
 
169
    }
 
170
 
 
171
  if (keywords)
 
172
    {
 
173
      const char *fmt;
 
174
      const char *author;
 
175
 
 
176
      if (local_mod)
 
177
        {
 
178
          /* For locally modified files, we'll append an 'M'
 
179
             to the revision number, and set the author to
 
180
             "(local)" since we can't always determine the
 
181
             current user's username */
 
182
          fmt = "%ldM";
 
183
          author = _("(local)");
 
184
        }
 
185
      else
 
186
        {
 
187
          fmt = "%ld";
 
188
          author = entry->cmt_author;
 
189
        }
 
190
      
 
191
      SVN_ERR (svn_subst_build_keywords 
 
192
               (&kw, keywords->data, 
 
193
                apr_psprintf (pool, fmt, entry->cmt_rev),
 
194
                entry->url, tm, author, pool));
 
195
    }
 
196
 
 
197
  SVN_ERR (svn_subst_copy_and_translate2 (base, to, eol, FALSE,
 
198
                                          &kw, TRUE,
 
199
                                          special ? TRUE : FALSE,
 
200
                                          pool));
 
201
  if (executable)
 
202
    SVN_ERR (svn_io_set_file_executable (to, TRUE, 
 
203
                                         FALSE, pool));
 
204
 
 
205
  if (! special)
 
206
    SVN_ERR (svn_io_set_file_affected_time (tm, to, pool));
 
207
 
 
208
  return SVN_NO_ERROR;
 
209
}
 
210
 
 
211
static svn_error_t *
 
212
copy_versioned_files (const char *from,
 
213
                      const char *to,
 
214
                      svn_opt_revision_t *revision,
 
215
                      svn_boolean_t force,
 
216
                      svn_boolean_t recurse,
 
217
                      const char *native_eol,
 
218
                      svn_client_ctx_t *ctx,
 
219
                      apr_pool_t *pool)
 
220
{
 
221
  svn_wc_adm_access_t *adm_access;
 
222
  const svn_wc_entry_t *entry;
 
223
  svn_error_t *err;
 
224
  apr_pool_t *iterpool;
 
225
  apr_hash_t *entries;
 
226
  apr_hash_index_t *hi;
 
227
  apr_finfo_t finfo;
 
228
 
 
229
  SVN_ERR (svn_wc_adm_probe_open3 (&adm_access, NULL, from, FALSE,
 
230
                                   0, ctx->cancel_func, ctx->cancel_baton,
 
231
                                   pool));
 
232
 
 
233
  SVN_ERR (svn_wc_entry (&entry, from, adm_access, FALSE, pool));
 
234
 
 
235
  /* Bail if we're trying to export something that doesn't exist,
 
236
     or isn't under version control. */
 
237
  if (! entry)
 
238
    {
 
239
      SVN_ERR (svn_wc_adm_close (adm_access));
 
240
      return svn_error_createf (SVN_ERR_UNVERSIONED_RESOURCE, NULL,
 
241
                                _("'%s' is not under version control "
 
242
                                  "or doesn't exist"),
 
243
                                svn_path_local_style (from, pool));
 
244
    }
 
245
 
 
246
  /* Only export 'added' files when the revision is WORKING.
 
247
     Otherwise, skip the 'added' files, since they didn't exist
 
248
     in the BASE revision and don't have an associated text-base.
 
249
 
 
250
     Don't export 'deleted' files and directories unless it's a
 
251
     revision other than WORKING.  These files and directories
 
252
     don't really exist in WORKING. */
 
253
  if ((revision->kind != svn_opt_revision_working &&
 
254
       entry->schedule == svn_wc_schedule_add) ||
 
255
      (revision->kind == svn_opt_revision_working &&
 
256
       entry->schedule == svn_wc_schedule_delete))
 
257
    return SVN_NO_ERROR;
 
258
 
 
259
  if (entry->kind == svn_node_dir)
 
260
    {
 
261
      /* Try to make the new directory.  If this fails because the
 
262
         directory already exists, check our FORCE flag to see if we
 
263
         care. */
 
264
      SVN_ERR (svn_io_stat (&finfo, from, APR_FINFO_PROT, pool));
 
265
      err = svn_io_dir_make (to, finfo.protection, pool);
 
266
      if (err)
 
267
        {
 
268
          if (! APR_STATUS_IS_EEXIST (err->apr_err))
 
269
            return err;
 
270
          if (! force)
 
271
            SVN_ERR_W (err, _("Destination directory exists, and will not be "
 
272
                              "overwritten unless forced"));
 
273
          else
 
274
            svn_error_clear (err);
 
275
        }
 
276
 
 
277
      SVN_ERR (svn_wc_entries_read (&entries, adm_access, FALSE, pool));
 
278
 
 
279
      iterpool = svn_pool_create (pool);
 
280
      for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
 
281
        {
 
282
          const char *item;
 
283
          const void *key;
 
284
          void *val;
 
285
          
 
286
          svn_pool_clear (iterpool);
 
287
 
 
288
          apr_hash_this (hi, &key, NULL, &val);
 
289
          
 
290
          item = key;
 
291
          entry = val;
 
292
          
 
293
          if (ctx->cancel_func)
 
294
            SVN_ERR (ctx->cancel_func (ctx->cancel_baton));
 
295
          
 
296
          /* ### We could also invoke ctx->notify_func somewhere in
 
297
             ### here... Is it called for, though?  Not sure. */ 
 
298
          
 
299
          if (entry->kind == svn_node_dir)
 
300
            {
 
301
              if (strcmp (item, SVN_WC_ENTRY_THIS_DIR) == 0)
 
302
                {
 
303
                  ; /* skip this, it's the current directory that we're
 
304
                       handling now. */
 
305
                }
 
306
              else
 
307
                {
 
308
                  if (recurse)
 
309
                    {
 
310
                      const char *new_from = svn_path_join (from, item, 
 
311
                                                            iterpool);
 
312
                      const char *new_to = svn_path_join (to, item, iterpool);
 
313
                  
 
314
                      SVN_ERR (copy_versioned_files (new_from, new_to, 
 
315
                                                     revision, force, recurse,
 
316
                                                     native_eol, ctx,
 
317
                                                     iterpool));
 
318
                    }
 
319
                }
 
320
            }
 
321
          else if (entry->kind == svn_node_file)
 
322
            {
 
323
              const char *new_from = svn_path_join (from, item, iterpool);
 
324
              const char *new_to = svn_path_join (to, item, iterpool);
 
325
                  
 
326
              SVN_ERR (copy_one_versioned_file (new_from, new_to, adm_access,
 
327
                                                revision, native_eol,
 
328
                                                iterpool));
 
329
            }
 
330
        }
 
331
      svn_pool_destroy (iterpool);
 
332
    }
 
333
  else if (entry->kind == svn_node_file)
 
334
    {
 
335
      SVN_ERR (copy_one_versioned_file (from, to, adm_access, revision,
 
336
                                        native_eol, pool));
 
337
    }
 
338
 
 
339
  SVN_ERR (svn_wc_adm_close (adm_access));
 
340
  return SVN_NO_ERROR;
 
341
}
 
342
 
 
343
 
 
344
/* Abstraction of open_root.
 
345
 *
 
346
 * Create PATH if it does not exist and is not obstructed, and invoke
 
347
 * NOTIFY_FUNC with NOTIFY_BATON on PATH.
 
348
 *
 
349
 * If PATH exists but is a file, then error with SVN_ERR_WC_NOT_DIRECTORY.
 
350
 *
 
351
 * If PATH is a already a directory, then error with
 
352
 * SVN_ERR_WC_OBSTRUCTED_UPDATE, unless FORCE, in which case just
 
353
 * export into PATH with no error.
 
354
 */
 
355
static svn_error_t *
 
356
open_root_internal (const char *path,
 
357
                    svn_boolean_t force,
 
358
                    svn_wc_notify_func2_t notify_func,
 
359
                    void *notify_baton,
 
360
                    apr_pool_t *pool)
 
361
{
 
362
  svn_node_kind_t kind;
 
363
  
 
364
  SVN_ERR (svn_io_check_path (path, &kind, pool));
 
365
  if (kind == svn_node_none)
 
366
    SVN_ERR (svn_io_dir_make (path, APR_OS_DEFAULT, pool));
 
367
  else if (kind == svn_node_file)
 
368
    return svn_error_createf (SVN_ERR_WC_NOT_DIRECTORY, NULL,
 
369
                              _("'%s' exists and is not a directory"),
 
370
                              svn_path_local_style (path, pool));
 
371
  else if ((kind != svn_node_dir) || (! force))
 
372
    return svn_error_createf (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
 
373
                              _("'%s' already exists"),
 
374
                              svn_path_local_style (path, pool));
 
375
 
 
376
  if (notify_func)
 
377
    {
 
378
      svn_wc_notify_t *notify = svn_wc_create_notify (path,
 
379
                                                      svn_wc_notify_update_add,
 
380
                                                      pool);
 
381
      notify->kind = svn_node_dir;
 
382
      (*notify_func) (notify_baton, notify, pool);
 
383
    }
 
384
  
 
385
  return SVN_NO_ERROR;
 
386
}
 
387
 
 
388
 
 
389
/* ---------------------------------------------------------------------- */
 
390
 
 
391
/*** A dedicated 'export' editor, which does no .svn/ accounting.  ***/
 
392
 
 
393
 
 
394
struct edit_baton
 
395
{
 
396
  const char *root_path;
 
397
  const char *root_url;
 
398
  svn_boolean_t force;
 
399
  svn_revnum_t *target_revision;
 
400
  apr_hash_t *externals;
 
401
  const char *native_eol;
 
402
 
 
403
  svn_wc_notify_func2_t notify_func;
 
404
  void *notify_baton;
 
405
};
 
406
 
 
407
 
 
408
struct dir_baton
 
409
{
 
410
  struct edit_baton *edit_baton;
 
411
  const char *path;
 
412
};
 
413
 
 
414
 
 
415
struct file_baton
 
416
{
 
417
  struct edit_baton *edit_baton;
 
418
 
 
419
  const char *path;
 
420
  const char *tmppath;
 
421
 
 
422
  /* We need to keep this around so we can explicitly close it in close_file, 
 
423
     thus flushing its output to disk so we can copy and translate it. */
 
424
  apr_file_t *tmp_file;
 
425
 
 
426
  /* The MD5 digest of the file's fulltext.  This is all zeros until
 
427
     the last textdelta window handler call returns. */
 
428
  unsigned char text_digest[APR_MD5_DIGESTSIZE];
 
429
 
 
430
  /* The three svn: properties we might actually care about. */
 
431
  const svn_string_t *eol_style_val;
 
432
  const svn_string_t *keywords_val;
 
433
  const svn_string_t *executable_val;
 
434
  svn_boolean_t special;
 
435
 
 
436
  /* Any keyword vals to be substituted */
 
437
  const char *revision;
 
438
  const char *url;
 
439
  const char *author;
 
440
  apr_time_t date;
 
441
 
 
442
  /* Pool associated with this baton. */
 
443
  apr_pool_t *pool;
 
444
};
 
445
 
 
446
 
 
447
struct handler_baton
 
448
{
 
449
  svn_txdelta_window_handler_t apply_handler;
 
450
  void *apply_baton;
 
451
  apr_pool_t *pool;
 
452
  const char *tmppath;
 
453
};
 
454
 
 
455
 
 
456
static svn_error_t *
 
457
set_target_revision (void *edit_baton, 
 
458
                     svn_revnum_t target_revision,
 
459
                     apr_pool_t *pool)
 
460
{
 
461
  struct edit_baton *eb = edit_baton;
 
462
 
 
463
  /* Stashing a target_revision in the baton */
 
464
  *(eb->target_revision) = target_revision;
 
465
  return SVN_NO_ERROR;
 
466
}
 
467
 
 
468
 
 
469
 
 
470
/* Just ensure that the main export directory exists. */
 
471
static svn_error_t *
 
472
open_root (void *edit_baton,
 
473
           svn_revnum_t base_revision,
 
474
           apr_pool_t *pool,
 
475
           void **root_baton)
 
476
{
 
477
  struct edit_baton *eb = edit_baton;  
 
478
  struct dir_baton *db = apr_pcalloc (pool, sizeof (*db));
 
479
 
 
480
  SVN_ERR (open_root_internal (eb->root_path, eb->force,
 
481
                               eb->notify_func, eb->notify_baton, pool));
 
482
 
 
483
  /* Build our dir baton. */
 
484
  db->path = eb->root_path;
 
485
  db->edit_baton = eb;
 
486
  *root_baton = db;
 
487
  
 
488
  return SVN_NO_ERROR;
 
489
}
 
490
 
 
491
 
 
492
/* Ensure the directory exists, and send feedback. */
 
493
static svn_error_t *
 
494
add_directory (const char *path,
 
495
               void *parent_baton,
 
496
               const char *copyfrom_path,
 
497
               svn_revnum_t copyfrom_revision,
 
498
               apr_pool_t *pool,
 
499
               void **baton)
 
500
{
 
501
  struct dir_baton *pb = parent_baton;
 
502
  struct dir_baton *db = apr_pcalloc (pool, sizeof (*db));
 
503
  struct edit_baton *eb = pb->edit_baton;
 
504
  const char *full_path = svn_path_join (eb->root_path, path, pool);
 
505
  svn_node_kind_t kind;
 
506
 
 
507
  SVN_ERR (svn_io_check_path (full_path, &kind, pool));
 
508
  if (kind == svn_node_none)
 
509
    SVN_ERR (svn_io_dir_make (full_path, APR_OS_DEFAULT, pool));
 
510
  else if (kind == svn_node_file)
 
511
    return svn_error_createf (SVN_ERR_WC_NOT_DIRECTORY, NULL,
 
512
                              _("'%s' exists and is not a directory"),
 
513
                              svn_path_local_style (full_path, pool));
 
514
  else if (! (kind == svn_node_dir && eb->force))
 
515
    return svn_error_createf (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
 
516
                              _("'%s' already exists"),
 
517
                              svn_path_local_style (full_path, pool));
 
518
 
 
519
  if (eb->notify_func)
 
520
    {
 
521
      svn_wc_notify_t *notify = svn_wc_create_notify (full_path,
 
522
                                                      svn_wc_notify_update_add,
 
523
                                                      pool);
 
524
      notify->kind = svn_node_dir;
 
525
      (*eb->notify_func) (eb->notify_baton, notify, pool);
 
526
    }
 
527
  
 
528
  /* Build our dir baton. */
 
529
  db->path = full_path;
 
530
  db->edit_baton = eb;
 
531
  *baton = db;
 
532
 
 
533
  return SVN_NO_ERROR;
 
534
}
 
535
 
 
536
 
 
537
/* Build a file baton. */
 
538
static svn_error_t *
 
539
add_file (const char *path,
 
540
          void *parent_baton,
 
541
          const char *copyfrom_path,
 
542
          svn_revnum_t copyfrom_revision,
 
543
          apr_pool_t *pool,
 
544
          void **baton)
 
545
{
 
546
  struct dir_baton *pb = parent_baton;
 
547
  struct edit_baton *eb = pb->edit_baton;
 
548
  struct file_baton *fb = apr_pcalloc (pool, sizeof(*fb));
 
549
  const char *full_path = svn_path_join (eb->root_path, path, pool);
 
550
  const char *full_url = svn_path_join (eb->root_url, path, pool);
 
551
 
 
552
  fb->edit_baton = eb;
 
553
  fb->path = full_path;
 
554
  fb->url = full_url;
 
555
  fb->pool = pool;
 
556
 
 
557
  *baton = fb;
 
558
  return SVN_NO_ERROR;
 
559
}
 
560
 
 
561
 
 
562
static svn_error_t *
 
563
window_handler (svn_txdelta_window_t *window, void *baton)
 
564
{
 
565
  struct handler_baton *hb = baton;
 
566
  svn_error_t *err;
 
567
 
 
568
  err = hb->apply_handler (window, hb->apply_baton);
 
569
  if (err)
 
570
    {
 
571
      /* We failed to apply the patch; clean up the temporary file.  */
 
572
      apr_file_remove (hb->tmppath, hb->pool);
 
573
    }
 
574
 
 
575
  return err;
 
576
}
 
577
 
 
578
 
 
579
 
 
580
/* Write incoming data into the tmpfile stream */
 
581
static svn_error_t *
 
582
apply_textdelta (void *file_baton,
 
583
                 const char *base_checksum,
 
584
                 apr_pool_t *pool,
 
585
                 svn_txdelta_window_handler_t *handler,
 
586
                 void **handler_baton)
 
587
{
 
588
  struct file_baton *fb = file_baton;
 
589
  struct handler_baton *hb = apr_palloc (pool, sizeof (*hb));
 
590
 
 
591
  SVN_ERR (svn_io_open_unique_file (&fb->tmp_file, &(fb->tmppath),
 
592
                                    fb->path, ".tmp", FALSE, fb->pool));
 
593
 
 
594
  hb->pool = pool;
 
595
  hb->tmppath = fb->tmppath;
 
596
 
 
597
  svn_txdelta_apply (svn_stream_empty (pool),
 
598
                     svn_stream_from_aprfile (fb->tmp_file, pool),
 
599
                     fb->text_digest, NULL, pool,
 
600
                     &hb->apply_handler, &hb->apply_baton);
 
601
 
 
602
  *handler_baton = hb;
 
603
  *handler = window_handler;
 
604
  return SVN_NO_ERROR;
 
605
}
 
606
 
 
607
 
 
608
static svn_error_t *
 
609
change_file_prop (void *file_baton,
 
610
                  const char *name,
 
611
                  const svn_string_t *value,
 
612
                  apr_pool_t *pool)
 
613
{
 
614
  struct file_baton *fb = file_baton;
 
615
 
 
616
  if (! value)
 
617
    return SVN_NO_ERROR;
 
618
 
 
619
  /* Store only the magic three properties. */
 
620
  if (strcmp (name, SVN_PROP_EOL_STYLE) == 0)
 
621
    fb->eol_style_val = svn_string_dup (value, fb->pool);
 
622
 
 
623
  else if (strcmp (name, SVN_PROP_KEYWORDS) == 0)
 
624
    fb->keywords_val = svn_string_dup (value, fb->pool);
 
625
 
 
626
  else if (strcmp (name, SVN_PROP_EXECUTABLE) == 0)
 
627
    fb->executable_val = svn_string_dup (value, fb->pool);
 
628
 
 
629
  /* Try to fill out the baton's keywords-structure too. */
 
630
  else if (strcmp (name, SVN_PROP_ENTRY_COMMITTED_REV) == 0)
 
631
    fb->revision = apr_pstrdup (fb->pool, value->data);
 
632
 
 
633
  else if (strcmp (name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0)
 
634
      SVN_ERR (svn_time_from_cstring (&fb->date, value->data, fb->pool));
 
635
 
 
636
  else if (strcmp (name, SVN_PROP_ENTRY_LAST_AUTHOR) == 0)
 
637
    fb->author = apr_pstrdup (fb->pool, value->data);
 
638
 
 
639
  else if (strcmp (name, SVN_PROP_SPECIAL) == 0)
 
640
    fb->special = TRUE;
 
641
 
 
642
  return SVN_NO_ERROR;
 
643
}
 
644
 
 
645
 
 
646
static svn_error_t *
 
647
change_dir_prop (void *dir_baton,
 
648
                 const char *name,
 
649
                 const svn_string_t *value,
 
650
                 apr_pool_t *pool)
 
651
{
 
652
  struct dir_baton *db = dir_baton;
 
653
  struct edit_baton *eb = db->edit_baton;
 
654
 
 
655
  if (value && (strcmp (name, SVN_PROP_EXTERNALS) == 0))
 
656
    add_externals (eb->externals, db->path, value);
 
657
 
 
658
  return SVN_NO_ERROR;
 
659
}
 
660
 
 
661
 
 
662
/* Move the tmpfile to file, and send feedback. */
 
663
static svn_error_t *
 
664
close_file (void *file_baton,
 
665
            const char *text_checksum,
 
666
            apr_pool_t *pool)
 
667
{
 
668
  struct file_baton *fb = file_baton;
 
669
  struct edit_baton *eb = fb->edit_baton;
 
670
 
 
671
  /* Was a txdelta even sent? */
 
672
  if (! fb->tmppath)
 
673
    return SVN_NO_ERROR;
 
674
 
 
675
  SVN_ERR (svn_io_file_close (fb->tmp_file, fb->pool));
 
676
 
 
677
  if (text_checksum)
 
678
    {
 
679
      const char *actual_checksum
 
680
        = svn_md5_digest_to_cstring (fb->text_digest, pool);
 
681
 
 
682
      if (actual_checksum && (strcmp (text_checksum, actual_checksum) != 0))
 
683
        {
 
684
          return svn_error_createf
 
685
            (SVN_ERR_CHECKSUM_MISMATCH, NULL,
 
686
             _("Checksum mismatch for '%s'; expected: '%s', actual: '%s'"),
 
687
             svn_path_local_style (fb->path, pool),
 
688
             text_checksum, actual_checksum);
 
689
        }
 
690
    }
 
691
 
 
692
  if ((! fb->eol_style_val) && (! fb->keywords_val) && (! fb->special))
 
693
    {
 
694
      SVN_ERR (svn_io_file_rename (fb->tmppath, fb->path, pool));
 
695
    }
 
696
  else
 
697
    {
 
698
      svn_subst_eol_style_t style;
 
699
      const char *eol;
 
700
      svn_subst_keywords_t final_kw = {0};
 
701
 
 
702
      if (fb->eol_style_val)
 
703
        SVN_ERR (get_eol_style (&style, &eol, fb->eol_style_val->data, 
 
704
                                eb->native_eol));
 
705
 
 
706
      if (fb->keywords_val)
 
707
        SVN_ERR (svn_subst_build_keywords (&final_kw, fb->keywords_val->data, 
 
708
                                           fb->revision, fb->url, fb->date, 
 
709
                                           fb->author, pool));
 
710
 
 
711
      SVN_ERR (svn_subst_copy_and_translate2
 
712
               (fb->tmppath, fb->path,
 
713
                fb->eol_style_val ? eol : NULL,
 
714
                fb->eol_style_val ? TRUE : FALSE, /* repair */
 
715
                fb->keywords_val ? &final_kw : NULL,
 
716
                TRUE, /* expand */
 
717
                fb->special,
 
718
                pool));
 
719
 
 
720
      SVN_ERR (svn_io_remove_file (fb->tmppath, pool));
 
721
    }
 
722
      
 
723
  if (fb->executable_val)
 
724
    SVN_ERR (svn_io_set_file_executable (fb->path, TRUE, FALSE, pool));
 
725
 
 
726
  if (fb->date && (! fb->special))
 
727
    SVN_ERR (svn_io_set_file_affected_time (fb->date, fb->path, pool));
 
728
 
 
729
  if (fb->edit_baton->notify_func)
 
730
    {
 
731
      svn_wc_notify_t *notify = svn_wc_create_notify (fb->path,
 
732
                                                      svn_wc_notify_update_add,
 
733
                                                      pool);
 
734
      notify->kind = svn_node_file;
 
735
      (*fb->edit_baton->notify_func) (fb->edit_baton->notify_baton, notify,
 
736
                                      pool);
 
737
    }
 
738
 
 
739
  return SVN_NO_ERROR;
 
740
}
 
741
 
 
742
 
 
743
 
 
744
/*** Public Interfaces ***/
 
745
 
 
746
svn_error_t *
 
747
svn_client_export3 (svn_revnum_t *result_rev,
 
748
                    const char *from,
 
749
                    const char *to,
 
750
                    const svn_opt_revision_t *peg_revision,
 
751
                    const svn_opt_revision_t *revision,
 
752
                    svn_boolean_t overwrite, 
 
753
                    svn_boolean_t ignore_externals,
 
754
                    svn_boolean_t recurse,
 
755
                    const char *native_eol,
 
756
                    svn_client_ctx_t *ctx,
 
757
                    apr_pool_t *pool)
 
758
{
 
759
  svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
 
760
  const char *url;
 
761
 
 
762
  if (svn_path_is_url (from) ||
 
763
      ! (revision->kind == svn_opt_revision_base ||
 
764
         revision->kind == svn_opt_revision_committed ||
 
765
         revision->kind == svn_opt_revision_working ||
 
766
         revision->kind == svn_opt_revision_unspecified))
 
767
    {
 
768
      svn_revnum_t revnum;
 
769
      svn_ra_session_t *ra_session;
 
770
      svn_node_kind_t kind;
 
771
      struct edit_baton *eb = apr_pcalloc (pool, sizeof (*eb));
 
772
 
 
773
      /* Get the RA connection. */
 
774
      SVN_ERR (svn_client__ra_session_from_path (&ra_session, &revnum,
 
775
                                                 &url, from, peg_revision,
 
776
                                                 revision, ctx, pool));
 
777
 
 
778
      eb->root_path = to;
 
779
      eb->root_url = url;
 
780
      eb->force = overwrite;
 
781
      eb->target_revision = &edit_revision;
 
782
      eb->notify_func = ctx->notify_func2;
 
783
      eb->notify_baton = ctx->notify_baton2;
 
784
      eb->externals = apr_hash_make (pool);
 
785
      eb->native_eol = native_eol; 
 
786
 
 
787
      SVN_ERR (svn_ra_check_path (ra_session, "", revnum, &kind, pool));
 
788
 
 
789
      if (kind == svn_node_file)
 
790
        {
 
791
          apr_hash_t *props;
 
792
          apr_hash_index_t *hi;
 
793
          struct file_baton *fb = apr_pcalloc (pool, sizeof(*fb));
 
794
 
 
795
          /* Since you cannot actually root an editor at a file, we 
 
796
           * manually drive a few functions of our editor. */
 
797
 
 
798
          /* This is the equivalent of a parentless add_file(). */
 
799
          fb->edit_baton = eb;
 
800
          fb->path = eb->root_path;
 
801
          fb->url = eb->root_url;
 
802
          fb->pool = pool;
 
803
          
 
804
          /* Copied from apply_textdelta(). */
 
805
          SVN_ERR (svn_io_open_unique_file (&fb->tmp_file, &(fb->tmppath),
 
806
                                            fb->path, ".tmp", FALSE,
 
807
                                            fb->pool));
 
808
 
 
809
          /* Step outside the editor-likeness for a moment, to actually talk
 
810
           * to the repository. */
 
811
          SVN_ERR (svn_ra_get_file (ra_session, "", revnum,
 
812
                                    svn_stream_from_aprfile (fb->tmp_file,
 
813
                                                             pool),
 
814
                                    NULL, &props, pool));
 
815
 
 
816
          /* Push the props into change_file_prop(), to update the file_baton
 
817
           * with information. */
 
818
          for (hi = apr_hash_first (pool, props); hi; hi = apr_hash_next (hi))
 
819
            {
 
820
              const void *key;
 
821
              void *val;
 
822
              apr_hash_this (hi, &key, NULL, &val);
 
823
              SVN_ERR (change_file_prop (fb, key, val, pool));
 
824
            }
 
825
          
 
826
          /* And now just use close_file() to do all the keyword and EOL
 
827
           * work, and put the file into place. */
 
828
          SVN_ERR (close_file (fb, NULL, pool));
 
829
        }
 
830
      else if (kind == svn_node_dir)
 
831
        {
 
832
          void *edit_baton;
 
833
          const svn_delta_editor_t *export_editor;
 
834
          const svn_ra_reporter2_t *reporter;
 
835
          void *report_baton;
 
836
          svn_delta_editor_t *editor = svn_delta_default_editor (pool);
 
837
          svn_boolean_t use_sleep = FALSE;
 
838
 
 
839
          editor->set_target_revision = set_target_revision;
 
840
          editor->open_root = open_root;
 
841
          editor->add_directory = add_directory;
 
842
          editor->add_file = add_file;
 
843
          editor->apply_textdelta = apply_textdelta;
 
844
          editor->close_file = close_file;
 
845
          editor->change_file_prop = change_file_prop;
 
846
          editor->change_dir_prop = change_dir_prop;
 
847
          
 
848
          SVN_ERR (svn_delta_get_cancellation_editor (ctx->cancel_func,
 
849
                                                      ctx->cancel_baton,
 
850
                                                      editor,
 
851
                                                      eb,
 
852
                                                      &export_editor,
 
853
                                                      &edit_baton,
 
854
                                                      pool));
 
855
      
 
856
      
 
857
          /* Manufacture a basic 'report' to the update reporter. */
 
858
          SVN_ERR (svn_ra_do_update (ra_session,
 
859
                                      &reporter, &report_baton,
 
860
                                      revnum,
 
861
                                      "", /* no sub-target */
 
862
                                      recurse,
 
863
                                      export_editor, edit_baton, pool));
 
864
 
 
865
          SVN_ERR (reporter->set_path (report_baton, "", revnum,
 
866
                                       TRUE, /* "help, my dir is empty!" */
 
867
                                       NULL, pool));
 
868
 
 
869
          SVN_ERR (reporter->finish_report (report_baton, pool));
 
870
 
 
871
          /* Special case: Due to our sly export/checkout method of
 
872
           * updating an empty directory, no target will have been created
 
873
           * if the exported item is itself an empty directory
 
874
           * (export_editor->open_root never gets called, because there
 
875
           * are no "changes" to make to the empty dir we reported to the
 
876
           * repository).
 
877
           *
 
878
           * So we just create the empty dir manually; but we do it via
 
879
           * open_root_internal(), in order to get proper notification.
 
880
           */
 
881
          SVN_ERR (svn_io_check_path (to, &kind, pool));
 
882
          if (kind == svn_node_none)
 
883
            SVN_ERR (open_root_internal
 
884
                     (to, overwrite, ctx->notify_func2, 
 
885
                      ctx->notify_baton2, pool));
 
886
 
 
887
          if (! ignore_externals && recurse)
 
888
            SVN_ERR (svn_client__fetch_externals (eb->externals, TRUE, 
 
889
                                                  &use_sleep, ctx, pool));
 
890
        }
 
891
    }
 
892
  else
 
893
    {
 
894
      svn_opt_revision_t working_revision = *revision;
 
895
      /* This is a working copy export. */
 
896
      if (working_revision.kind == svn_opt_revision_unspecified)
 
897
        {
 
898
          /* Default to WORKING in the case that we have
 
899
             been given a working copy path */
 
900
          working_revision.kind = svn_opt_revision_working;
 
901
        }
 
902
      
 
903
      /* just copy the contents of the working copy into the target path. */
 
904
      SVN_ERR (copy_versioned_files (from, to, &working_revision, overwrite, 
 
905
                                     recurse, native_eol, ctx, pool));
 
906
    }
 
907
  
 
908
 
 
909
  if (ctx->notify_func2)
 
910
    {
 
911
      svn_wc_notify_t *notify
 
912
        = svn_wc_create_notify (to,
 
913
                                svn_wc_notify_update_completed, pool);
 
914
      notify->revision = edit_revision;
 
915
      (*ctx->notify_func2) (ctx->notify_baton2, notify, pool);
 
916
    }
 
917
 
 
918
  if (result_rev)
 
919
    *result_rev = edit_revision;
 
920
 
 
921
  return SVN_NO_ERROR;
 
922
}
 
923
 
 
924
 
 
925
svn_error_t *
 
926
svn_client_export2 (svn_revnum_t *result_rev,
 
927
                    const char *from,
 
928
                    const char *to,
 
929
                    svn_opt_revision_t *revision,
 
930
                    svn_boolean_t force, 
 
931
                    const char *native_eol,
 
932
                    svn_client_ctx_t *ctx,
 
933
                    apr_pool_t *pool)
 
934
{
 
935
  svn_opt_revision_t peg_revision;
 
936
 
 
937
  peg_revision.kind = svn_opt_revision_unspecified;
 
938
 
 
939
  return svn_client_export3 (result_rev, from, to, &peg_revision,
 
940
                             revision, force, FALSE, TRUE,
 
941
                             native_eol, ctx, pool);
 
942
}
 
943
 
 
944
  
 
945
svn_error_t *
 
946
svn_client_export (svn_revnum_t *result_rev,
 
947
                   const char *from,
 
948
                   const char *to,
 
949
                   svn_opt_revision_t *revision,
 
950
                   svn_boolean_t force, 
 
951
                   svn_client_ctx_t *ctx,
 
952
                   apr_pool_t *pool)
 
953
{
 
954
  return svn_client_export2 (result_rev, from, to, revision, force, NULL, ctx,
 
955
                             pool);
 
956
}