~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_wc/log.c

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * log.c:  handle the adm area's log file.
 
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
#include <string.h>
 
22
 
 
23
#include <apr_pools.h>
 
24
#include <apr_strings.h>
 
25
 
 
26
#include "svn_wc.h"
 
27
#include "svn_error.h"
 
28
#include "svn_string.h"
 
29
#include "svn_xml.h"
 
30
#include "svn_pools.h"
 
31
#include "svn_io.h"
 
32
#include "svn_path.h"
 
33
#include "svn_time.h"
 
34
 
 
35
#include "wc.h"
 
36
#include "log.h"
 
37
#include "props.h"
 
38
#include "adm_files.h"
 
39
#include "entries.h"
 
40
#include "translate.h"
 
41
#include "questions.h"
 
42
 
 
43
#include "svn_private_config.h"
 
44
 
 
45
 
 
46
/*** Userdata for the callbacks. ***/
 
47
struct log_runner
 
48
{
 
49
  apr_pool_t *pool;
 
50
  svn_xml_parser_t *parser;
 
51
  svn_boolean_t entries_modified;
 
52
  svn_wc_adm_access_t *adm_access;  /* the dir in which all this happens */
 
53
  const char *diff3_cmd;            /* external diff3 cmd, or null if none */
 
54
 
 
55
  /* Which top-level log element we're on for this logfile.  Some
 
56
     callers care whether a failure happened on the first element or
 
57
     on some later element (e.g., 'svn cleanup').
 
58
 
 
59
     This is initialized to 0 when the log_runner is created, and
 
60
     incremented every time start_handler() is called. */
 
61
  int count;
 
62
};
 
63
 
 
64
 
 
65
 
 
66
/*** The XML handlers. ***/
 
67
 
 
68
/* Used by file_xfer_under_path(). */
 
69
enum svn_wc__xfer_action {
 
70
  svn_wc__xfer_cp,
 
71
  svn_wc__xfer_mv,
 
72
  svn_wc__xfer_append,
 
73
  svn_wc__xfer_cp_and_translate,
 
74
  svn_wc__xfer_cp_and_detranslate
 
75
};
 
76
 
 
77
 
 
78
/* Perform some sort of copy-related ACTION on NAME and DEST:
 
79
 
 
80
      svn_wc__xfer_cp:                 just do a copy of NAME to DEST.
 
81
      svn_wc__xfer_mv:                 do a copy, then remove NAME.
 
82
      svn_wc__xfer_append:             append contents of NAME to DEST
 
83
      svn_wc__xfer_cp_and_translate:   copy NAME to DEST, doing any eol
 
84
                                       and keyword expansion according to
 
85
                                       the current property vals of DEST.
 
86
      svn_wc__xfer_cp_and_detranslate: copy NAME to DEST, converting to LF
 
87
                                       and contracting keywords according to
 
88
                                       the current property vals of NAME.
 
89
*/
 
90
static svn_error_t *
 
91
file_xfer_under_path (svn_wc_adm_access_t *adm_access,
 
92
                      const char *name,
 
93
                      const char *dest,
 
94
                      enum svn_wc__xfer_action action,
 
95
                      apr_pool_t *pool)
 
96
{
 
97
  svn_error_t *err;
 
98
  const char *full_from_path, *full_dest_path;
 
99
 
 
100
  full_from_path = svn_path_join (svn_wc_adm_access_path (adm_access), name,
 
101
                                  pool);
 
102
  full_dest_path = svn_path_join (svn_wc_adm_access_path (adm_access), dest,
 
103
                                  pool);
 
104
 
 
105
  switch (action)
 
106
    {
 
107
    case svn_wc__xfer_append:
 
108
      return svn_io_append_file (full_from_path, full_dest_path, pool);
 
109
      
 
110
    case svn_wc__xfer_cp:
 
111
      return svn_io_copy_file (full_from_path, full_dest_path, FALSE, pool);
 
112
 
 
113
    case svn_wc__xfer_cp_and_translate:
 
114
      {
 
115
        svn_subst_keywords_t *keywords;
 
116
        const char *eol_str;
 
117
        svn_boolean_t special;
 
118
 
 
119
        /* Note that this action takes properties from dest, not source. */
 
120
        SVN_ERR (svn_wc__get_keywords (&keywords, full_dest_path, adm_access,
 
121
                                       NULL, pool));
 
122
        SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, full_dest_path,
 
123
                                        adm_access, pool));
 
124
        SVN_ERR (svn_wc__get_special (&special, full_dest_path, adm_access,
 
125
                                      pool));
 
126
 
 
127
        SVN_ERR (svn_subst_copy_and_translate2 (full_from_path,
 
128
                                                full_dest_path,
 
129
                                                eol_str,
 
130
                                                TRUE,
 
131
                                                keywords,
 
132
                                                TRUE,
 
133
                                                special,
 
134
                                                pool));
 
135
 
 
136
        SVN_ERR (svn_wc__maybe_set_read_only (NULL, full_dest_path,
 
137
                                              adm_access, pool));
 
138
 
 
139
        /* After copying, set the file executable if props dictate. */
 
140
        return svn_wc__maybe_set_executable (NULL, full_dest_path, adm_access,
 
141
                                             pool);
 
142
      }
 
143
 
 
144
    case svn_wc__xfer_cp_and_detranslate:
 
145
      {
 
146
        svn_subst_keywords_t *keywords;
 
147
        const char *eol_str;
 
148
        svn_boolean_t special;
 
149
 
 
150
        /* Note that this action takes properties from source, not dest. */
 
151
        SVN_ERR (svn_wc__get_keywords (&keywords, full_from_path, adm_access,
 
152
                                       NULL, pool));
 
153
        SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, full_from_path,
 
154
                                        adm_access, pool));
 
155
        SVN_ERR (svn_wc__get_special (&special, full_from_path, adm_access,
 
156
                                      pool));
 
157
 
 
158
        /* If any specific eol style was indicated, then detranslate
 
159
           back to repository normal form ("\n"), repairingly.  But if
 
160
           no style indicated, don't touch line endings at all. */
 
161
        return svn_subst_copy_and_translate2 (full_from_path,
 
162
                                              full_dest_path,
 
163
                                              (eol_str ? "\n" : NULL),
 
164
                                              (eol_str ? TRUE : FALSE),  
 
165
                                              keywords,
 
166
                                              FALSE, /* contract keywords */
 
167
                                              special,
 
168
                                              pool);
 
169
      }
 
170
 
 
171
    case svn_wc__xfer_mv:
 
172
      SVN_ERR (svn_wc__prep_file_for_replacement (full_dest_path, TRUE, pool));
 
173
 
 
174
      err = svn_io_file_rename (full_from_path,
 
175
                                full_dest_path, pool);
 
176
 
 
177
      /* If we got an ENOENT, that's ok;  the move has probably
 
178
         already completed in an earlier run of this log.  */
 
179
      if (err)
 
180
        {
 
181
          if (! APR_STATUS_IS_ENOENT(err->apr_err))
 
182
            return svn_error_quick_wrap (err, _("Can't move source to dest"));
 
183
          svn_error_clear (err);
 
184
        }
 
185
    }
 
186
 
 
187
  return SVN_NO_ERROR;
 
188
}
 
189
 
 
190
 
 
191
/* If new text was committed, then replace the text base for
 
192
 * newly-committed file NAME in directory PATH with the new
 
193
 * post-commit text base, which is waiting in the adm tmp area in
 
194
 * detranslated form.
 
195
 *
 
196
 * If eol and/or keyword translation would cause the working file to
 
197
 * change, then overwrite the working file with a translated copy of
 
198
 * the new text base (but only if the translated copy differs from the
 
199
 * current working file -- if they are the same, do nothing, to avoid
 
200
 * clobbering timestamps unnecessarily).
 
201
 *
 
202
 * If the executable property is set, the set working file's
 
203
 * executable.
 
204
 *
 
205
 * If the working file was re-translated or had executability set,
 
206
 * then set OVERWROTE_WORKING to TRUE.  If the working file isn't
 
207
 * touched at all, then set to FALSE.
 
208
 * 
 
209
 * Use POOL for any temporary allocation.
 
210
 */
 
211
static svn_error_t *
 
212
install_committed_file (svn_boolean_t *overwrote_working,
 
213
                        svn_wc_adm_access_t *adm_access,
 
214
                        const char *name,
 
215
                        apr_pool_t *pool)
 
216
{
 
217
  const char *filepath;
 
218
  const char *tmp_text_base;
 
219
  svn_node_kind_t kind;
 
220
  svn_subst_keywords_t *keywords;
 
221
  apr_file_t *ignored;
 
222
  svn_boolean_t same, did_set;
 
223
  const char *tmp_wfile, *pdir, *bname;
 
224
  const char *eol_str;
 
225
  svn_boolean_t special;
 
226
 
 
227
  /* start off assuming that the working file isn't touched. */
 
228
  *overwrote_working = FALSE;
 
229
 
 
230
  filepath = svn_path_join (svn_wc_adm_access_path (adm_access), name, pool);
 
231
 
 
232
  /* In the commit, newlines and keywords may have been
 
233
   * canonicalized and/or contracted... Or they may not have
 
234
   * been.  It's kind of hard to know.  Here's how we find out:
 
235
   *
 
236
   *    1. Make a translated tmp copy of the committed text base.
 
237
   *       Or, if no committed text base exists (the commit must have
 
238
   *       been a propchange only), make a translated tmp copy of the
 
239
   *       working file.
 
240
   *    2. Compare the translated tmpfile to the working file.
 
241
   *    3. If different, copy the tmpfile over working file.
 
242
   *
 
243
   * This means we only rewrite the working file if we absolutely
 
244
   * have to, which is good because it avoids changing the file's
 
245
   * timestamp unless necessary, so editors aren't tempted to
 
246
   * reread the file if they don't really need to.
 
247
   */
 
248
 
 
249
  /* start off getting the latest translation prop values. */
 
250
  SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, filepath, adm_access, pool));
 
251
  SVN_ERR (svn_wc__get_keywords (&keywords, filepath, adm_access, NULL, pool));
 
252
  SVN_ERR (svn_wc__get_special (&special, filepath, adm_access, pool));
 
253
 
 
254
  svn_path_split (filepath, &pdir, &bname, pool);
 
255
  tmp_wfile = svn_wc__adm_path (pdir, TRUE, pool, bname, NULL);
 
256
  
 
257
  SVN_ERR (svn_io_open_unique_file (&ignored, &tmp_wfile,
 
258
                                    tmp_wfile, SVN_WC__TMP_EXT,
 
259
                                    FALSE, pool));
 
260
  SVN_ERR (svn_io_file_close (ignored, pool));
 
261
 
 
262
  /* Is there a tmp_text_base that needs to be installed?  */
 
263
  tmp_text_base = svn_wc__text_base_path (filepath, 1, pool);
 
264
  SVN_ERR (svn_io_check_path (tmp_text_base, &kind, pool));
 
265
 
 
266
  if (kind == svn_node_file)
 
267
    SVN_ERR (svn_subst_copy_and_translate2 (tmp_text_base,
 
268
                                            tmp_wfile,
 
269
                                            eol_str,
 
270
                                            FALSE, /* don't repair eol */
 
271
                                            keywords,
 
272
                                            TRUE, /* expand keywords */
 
273
                                            special,
 
274
                                            pool));
 
275
  else
 
276
    SVN_ERR (svn_subst_copy_and_translate2 (filepath,
 
277
                                            tmp_wfile,
 
278
                                            eol_str,
 
279
                                            FALSE, /* don't repair eol */
 
280
                                            keywords,
 
281
                                            TRUE, /* expand keywords */
 
282
                                            special,
 
283
                                            pool));
 
284
 
 
285
  if (! special)
 
286
    {
 
287
      SVN_ERR (svn_io_files_contents_same_p (&same, tmp_wfile, filepath, pool));
 
288
    }
 
289
  else
 
290
    {
 
291
      same = TRUE;
 
292
    }
 
293
  
 
294
  if (! same)
 
295
    {
 
296
      SVN_ERR (svn_io_copy_file (tmp_wfile, filepath, FALSE, pool));
 
297
      *overwrote_working = TRUE;
 
298
    }
 
299
 
 
300
  SVN_ERR (svn_io_remove_file (tmp_wfile, pool));
 
301
 
 
302
  SVN_ERR (svn_wc__maybe_set_read_only (&did_set, filepath, adm_access, pool));
 
303
  if (did_set)
 
304
    /* the file may have been overwritten or its timestamp changed by
 
305
       setting it read-only */
 
306
    *overwrote_working = TRUE;
 
307
 
 
308
  /* Set the working file's execute bit if props dictate. */
 
309
  SVN_ERR (svn_wc__maybe_set_executable (&did_set, filepath, adm_access, pool));
 
310
  if (did_set)
 
311
    /* okay, so we didn't -overwrite- the working file, but we changed
 
312
       its timestamp, which is the point of returning this flag. :-) */
 
313
    *overwrote_working = TRUE;
 
314
 
 
315
  /* Install the new text base if one is waiting. */
 
316
  if (kind == svn_node_file)  /* tmp_text_base exists */
 
317
    SVN_ERR (svn_wc__sync_text_base (filepath, pool));
 
318
 
 
319
  return SVN_NO_ERROR;
 
320
}
 
321
 
 
322
 
 
323
/* Sometimes, documentation would only confuse matters. */
 
324
static apr_status_t
 
325
pick_error_code (struct log_runner *loggy)
 
326
{
 
327
  if (loggy->count <= 1)
 
328
    return SVN_ERR_WC_BAD_ADM_LOG_START;
 
329
  else
 
330
    return SVN_ERR_WC_BAD_ADM_LOG;
 
331
}
 
332
 
 
333
static void
 
334
signal_error (struct log_runner *loggy, svn_error_t *err)
 
335
{
 
336
  svn_xml_signal_bailout
 
337
    (svn_error_createf (pick_error_code (loggy), err,
 
338
                        _("In directory '%s'"),
 
339
                        svn_path_local_style (svn_wc_adm_access_path
 
340
                                              (loggy->adm_access),
 
341
                                              loggy->pool)),
 
342
     loggy->parser);
 
343
}
 
344
 
 
345
 
 
346
 
 
347
 
 
348
 
 
349
/*** Dispatch on the xml opening tag. ***/
 
350
 
 
351
static svn_error_t *
 
352
log_do_merge (struct log_runner *loggy,
 
353
              const char *name,
 
354
              const char **atts)
 
355
{
 
356
  const char *left, *right;
 
357
  const char *left_label, *right_label, *target_label;
 
358
  enum svn_wc_merge_outcome_t merge_outcome;
 
359
 
 
360
  /* NAME is the basename of our merge_target.  Pull out LEFT and RIGHT. */
 
361
  left = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_1, atts);
 
362
  if (! left)
 
363
    return svn_error_createf (pick_error_code (loggy), NULL,
 
364
                              _("Missing 'left' attribute in '%s'"),
 
365
                              svn_path_local_style
 
366
                              (svn_wc_adm_access_path (loggy->adm_access),
 
367
                               loggy->pool));
 
368
  right = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_2, atts);
 
369
  if (! right)
 
370
    return svn_error_createf (pick_error_code (loggy), NULL,
 
371
                              _("Missing 'right' attribute in '%s'"),
 
372
                              svn_path_local_style
 
373
                              (svn_wc_adm_access_path (loggy->adm_access),
 
374
                               loggy->pool));
 
375
 
 
376
  /* Grab all three labels too.  If non-existent, we'll end up passing
 
377
     NULLs to svn_wc_merge, which is fine -- it will use default
 
378
     labels. */
 
379
  left_label = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_3, atts);
 
380
  right_label = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_4, atts);
 
381
  target_label = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_5, atts);
 
382
 
 
383
  /* Convert the 3 basenames into full paths. */
 
384
  left = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), left,
 
385
                        loggy->pool);
 
386
  right = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), right,
 
387
                         loggy->pool);
 
388
  name = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
 
389
                        loggy->pool);
 
390
 
 
391
  /* Now do the merge with our full paths. */
 
392
  SVN_ERR (svn_wc_merge (left, right, name, loggy->adm_access,
 
393
                         left_label, right_label, target_label,
 
394
                         FALSE, &merge_outcome, loggy->diff3_cmd, 
 
395
                         loggy->pool));
 
396
 
 
397
  return SVN_NO_ERROR;
 
398
}
 
399
 
 
400
 
 
401
static svn_error_t *
 
402
log_do_file_xfer (struct log_runner *loggy,
 
403
                  const char *name,
 
404
                  enum svn_wc__xfer_action action,
 
405
                  const char **atts)
 
406
{
 
407
  svn_error_t *err;
 
408
  const char *dest = NULL;
 
409
 
 
410
  /* We have the name (src), and the destination is absolutely required. */
 
411
  dest = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_DEST, atts);
 
412
  if (! dest)
 
413
    return svn_error_createf (pick_error_code (loggy), NULL,
 
414
                              _("Missing 'dest' attribute in '%s'"),
 
415
                              svn_path_local_style
 
416
                              (svn_wc_adm_access_path (loggy->adm_access),
 
417
                               loggy->pool));
 
418
 
 
419
  err = file_xfer_under_path (loggy->adm_access, name, dest, action,
 
420
                              loggy->pool);
 
421
  if (err)
 
422
    signal_error (loggy, err);
 
423
 
 
424
  return SVN_NO_ERROR;
 
425
}
 
426
 
 
427
/* Make file NAME in log's CWD readonly */
 
428
static svn_error_t *
 
429
log_do_file_readonly (struct log_runner *loggy,
 
430
                      const char *name)
 
431
{
 
432
  const char *full_path
 
433
    = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
 
434
                     loggy->pool);
 
435
 
 
436
  SVN_ERR (svn_io_set_file_read_only (full_path, FALSE, loggy->pool));
 
437
 
 
438
  return SVN_NO_ERROR;
 
439
}
 
440
 
 
441
/* Maybe make file NAME in log's CWD readonly */
 
442
static svn_error_t *
 
443
log_do_file_maybe_readonly (struct log_runner *loggy,
 
444
                            const char *name)
 
445
{
 
446
  const char *full_path
 
447
    = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
 
448
                     loggy->pool);
 
449
 
 
450
  SVN_ERR (svn_wc__maybe_set_read_only (NULL, full_path, loggy->adm_access,
 
451
                                        loggy->pool));
 
452
 
 
453
  return SVN_NO_ERROR;
 
454
}
 
455
 
 
456
/* Set file NAME in log's CWD to timestamp value in ATTS. */
 
457
static svn_error_t *
 
458
log_do_file_timestamp (struct log_runner *loggy,
 
459
                       const char *name,                       
 
460
                       const char **atts)
 
461
{
 
462
  apr_time_t timestamp;
 
463
  svn_node_kind_t kind;
 
464
  const char *full_path
 
465
    = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
 
466
                     loggy->pool);
 
467
 
 
468
  const char *timestamp_string
 
469
    = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_TIMESTAMP, atts);
 
470
  svn_boolean_t is_special;
 
471
  
 
472
  if (! timestamp_string)
 
473
    return svn_error_createf (pick_error_code (loggy), NULL,
 
474
                              _("Missing 'timestamp' attribute in '%s'"),
 
475
                              svn_path_local_style
 
476
                              (svn_wc_adm_access_path (loggy->adm_access),
 
477
                               loggy->pool));
 
478
 
 
479
  /* Do not set the timestamp on special files. */
 
480
  SVN_ERR (svn_io_check_special_path (full_path, &kind, &is_special,
 
481
                                      loggy->pool));
 
482
  
 
483
  if (! is_special)
 
484
    {
 
485
      SVN_ERR (svn_time_from_cstring (&timestamp, timestamp_string,
 
486
                                      loggy->pool));
 
487
      
 
488
      SVN_ERR (svn_io_set_file_affected_time (timestamp, full_path,
 
489
                                              loggy->pool));
 
490
    }
 
491
 
 
492
  return SVN_NO_ERROR;
 
493
}
 
494
 
 
495
 
 
496
/* Remove file NAME in log's CWD. */
 
497
static svn_error_t *
 
498
log_do_rm (struct log_runner *loggy, const char *name)
 
499
{
 
500
  const char *full_path
 
501
    = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
 
502
                     name, loggy->pool);
 
503
 
 
504
  SVN_ERR (svn_io_remove_file (full_path, loggy->pool));
 
505
 
 
506
  return SVN_NO_ERROR;
 
507
}
 
508
 
 
509
 
 
510
 
 
511
 
 
512
static svn_error_t *
 
513
log_do_modify_entry (struct log_runner *loggy,
 
514
                     const char *name,
 
515
                     const char **atts)
 
516
{
 
517
  svn_error_t *err;
 
518
  apr_hash_t *ah = svn_xml_make_att_hash (atts, loggy->pool);
 
519
  const char *tfile;
 
520
  svn_wc_entry_t *entry;
 
521
  apr_uint32_t modify_flags;
 
522
  const char *valuestr;
 
523
 
 
524
  /* Convert the attributes into an entry structure. */
 
525
  SVN_ERR (svn_wc__atts_to_entry (&entry, &modify_flags, ah, loggy->pool));
 
526
 
 
527
  /* Make TFILE the path of the thing being modified.  */
 
528
  tfile = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
 
529
                         strcmp (name, SVN_WC_ENTRY_THIS_DIR) ? name : "",
 
530
                         loggy->pool);
 
531
      
 
532
  /* Did the log command give us any timestamps?  There are three
 
533
     possible scenarios here.  We must check both text_time
 
534
     and prop_time for each of the three scenarios.  */
 
535
 
 
536
  /* TEXT_TIME: */
 
537
  valuestr = apr_hash_get (ah, SVN_WC__ENTRY_ATTR_TEXT_TIME,
 
538
                           APR_HASH_KEY_STRING);
 
539
 
 
540
  if ((modify_flags & SVN_WC__ENTRY_MODIFY_TEXT_TIME)
 
541
      && (! strcmp (valuestr, SVN_WC_TIMESTAMP_WC)))
 
542
    {
 
543
      svn_node_kind_t tfile_kind;
 
544
      apr_time_t text_time;
 
545
 
 
546
      err = svn_io_check_path (tfile, &tfile_kind, loggy->pool);
 
547
      if (err)
 
548
        return svn_error_createf
 
549
          (pick_error_code (loggy), err,
 
550
           _("Error checking path '%s'"), svn_path_local_style (tfile,
 
551
                                                                loggy->pool));
 
552
          
 
553
      err = svn_io_file_affected_time (&text_time, tfile, loggy->pool);
 
554
      if (err)
 
555
        return svn_error_createf
 
556
          (pick_error_code (loggy), err,
 
557
           _("Error getting 'affected time' on '%s'"),
 
558
           svn_path_local_style (tfile, loggy->pool));
 
559
 
 
560
      entry->text_time = text_time;
 
561
    }
 
562
 
 
563
  /* PROP_TIME: */
 
564
  valuestr = apr_hash_get (ah, SVN_WC__ENTRY_ATTR_PROP_TIME, 
 
565
                           APR_HASH_KEY_STRING);
 
566
 
 
567
  if ((modify_flags & SVN_WC__ENTRY_MODIFY_PROP_TIME)
 
568
      && (! strcmp (valuestr, SVN_WC_TIMESTAMP_WC)))
 
569
    {
 
570
      const char *pfile;
 
571
      svn_node_kind_t pfile_kind;
 
572
      apr_time_t prop_time;
 
573
 
 
574
      err = svn_wc__prop_path (&pfile, tfile, loggy->adm_access, FALSE,
 
575
                               loggy->pool);
 
576
      if (err)
 
577
        signal_error (loggy, err);
 
578
      
 
579
      err = svn_io_check_path (pfile, &pfile_kind, loggy->pool);
 
580
      if (err)
 
581
        return svn_error_createf
 
582
          (pick_error_code (loggy), err,
 
583
           _("Error checking path '%s'"),
 
584
           svn_path_local_style (pfile, loggy->pool));
 
585
      
 
586
      err = svn_io_file_affected_time (&prop_time, pfile, loggy->pool);
 
587
      if (err)
 
588
        return svn_error_createf
 
589
          (pick_error_code (loggy), NULL,
 
590
           _("Error getting 'affected time' on '%s'"),
 
591
           svn_path_local_style (pfile, loggy->pool));
 
592
 
 
593
      entry->prop_time = prop_time;
 
594
    }
 
595
 
 
596
  /* Now write the new entry out */
 
597
  err = svn_wc__entry_modify (loggy->adm_access, name,
 
598
                              entry, modify_flags, FALSE, loggy->pool);
 
599
  if (err)
 
600
    return svn_error_createf (pick_error_code (loggy), err,
 
601
                              _("Error modifying entry for '%s'"), name);
 
602
  loggy->entries_modified = TRUE;
 
603
 
 
604
  return SVN_NO_ERROR;
 
605
}
 
606
 
 
607
static svn_error_t *
 
608
log_do_delete_lock (struct log_runner *loggy,
 
609
                    const char *name)
 
610
{
 
611
  svn_error_t *err;
 
612
  svn_wc_entry_t entry;
 
613
 
 
614
  entry.lock_token = entry.lock_comment = entry.lock_owner = NULL;
 
615
  entry.lock_creation_date = 0;
 
616
 
 
617
  /* Now write the new entry out */
 
618
  err = svn_wc__entry_modify (loggy->adm_access, name,
 
619
                              &entry,
 
620
                              SVN_WC__ENTRY_MODIFY_LOCK_TOKEN
 
621
                              | SVN_WC__ENTRY_MODIFY_LOCK_OWNER
 
622
                              | SVN_WC__ENTRY_MODIFY_LOCK_COMMENT
 
623
                              | SVN_WC__ENTRY_MODIFY_LOCK_CREATION_DATE,
 
624
                              FALSE, loggy->pool);
 
625
  if (err)
 
626
    return svn_error_createf (pick_error_code (loggy), err,
 
627
                              _("Error removing lock from entry for '%s'"),
 
628
                              name);
 
629
  loggy->entries_modified = TRUE;
 
630
 
 
631
  return SVN_NO_ERROR;
 
632
}
 
633
 
 
634
/* Ben sez:  this log command is (at the moment) only executed by the
 
635
   update editor.  It attempts to forcefully remove working data. */
 
636
static svn_error_t *
 
637
log_do_delete_entry (struct log_runner *loggy, const char *name)
 
638
{
 
639
  svn_wc_adm_access_t *adm_access;
 
640
  const svn_wc_entry_t *entry;
 
641
  svn_error_t *err = SVN_NO_ERROR;
 
642
  const char *full_path
 
643
    = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
 
644
                     loggy->pool);
 
645
 
 
646
  /* Figure out if 'name' is a dir or a file */
 
647
  SVN_ERR (svn_wc_adm_probe_retrieve (&adm_access, loggy->adm_access, full_path,
 
648
                                      loggy->pool));
 
649
  SVN_ERR (svn_wc_entry (&entry, full_path, adm_access, FALSE, loggy->pool));
 
650
 
 
651
  if (! entry)
 
652
    /* Hmm....this entry is already absent from the revision control
 
653
       system.  Chances are good that this item was removed via a
 
654
       commit from this working copy.  */
 
655
    return SVN_NO_ERROR;
 
656
 
 
657
  /* Remove the object from revision control -- whether it's a
 
658
     single file or recursive directory removal.  Attempt
 
659
     attempt to destroy all working files & dirs too. 
 
660
  
 
661
     ### We pass NULL, NULL for cancel_func and cancel_baton below.
 
662
     ### If they were available, it would be nice to use them. */
 
663
  if (entry->kind == svn_node_dir)
 
664
    {
 
665
      svn_wc_adm_access_t *ignored;
 
666
      
 
667
      /* If we get the right kind of error, it means the directory is
 
668
         already missing, so all we need to do is delete its entry in
 
669
         the parent directory. */
 
670
      err = svn_wc_adm_retrieve (&ignored, adm_access, full_path, loggy->pool);
 
671
      if (err)
 
672
        {
 
673
          if (err->apr_err == SVN_ERR_WC_NOT_LOCKED)
 
674
            {
 
675
              apr_hash_t *entries;
 
676
 
 
677
              svn_error_clear (err);
 
678
              err = SVN_NO_ERROR;
 
679
 
 
680
              if (entry->schedule != svn_wc_schedule_add)
 
681
                {
 
682
                  SVN_ERR (svn_wc_entries_read (&entries, loggy->adm_access,
 
683
                                                TRUE, loggy->pool));
 
684
                  svn_wc__entry_remove (entries, name);
 
685
                  SVN_ERR (svn_wc__entries_write (entries, loggy->adm_access, 
 
686
                                                  loggy->pool));
 
687
                }
 
688
            }
 
689
          else
 
690
            {
 
691
              return err;
 
692
            }
 
693
        }
 
694
      else 
 
695
        {
 
696
          err = svn_wc_remove_from_revision_control (adm_access,
 
697
                                                     SVN_WC_ENTRY_THIS_DIR,
 
698
                                                     TRUE, /* destroy */
 
699
                                                     FALSE, /* instant_error */
 
700
                                                     NULL, NULL,
 
701
                                                     loggy->pool);
 
702
        }
 
703
    }
 
704
  else if (entry->kind == svn_node_file)
 
705
    {
 
706
      err = svn_wc_remove_from_revision_control (loggy->adm_access, name,
 
707
                                                 TRUE, /* destroy */
 
708
                                                 FALSE, /* instant_error */
 
709
                                                 NULL, NULL,
 
710
                                                 loggy->pool);
 
711
    }
 
712
 
 
713
    if ((err) && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
 
714
      {
 
715
        svn_error_clear (err);
 
716
        return SVN_NO_ERROR;
 
717
      }
 
718
    else
 
719
        return err;
 
720
}
 
721
 
 
722
/* Note:  assuming that svn_wc__log_commit() is what created all of
 
723
   the <committed...> commands, the `name' attribute will either be a
 
724
   file or SVN_WC_ENTRY_THIS_DIR. */
 
725
static svn_error_t *
 
726
log_do_committed (struct log_runner *loggy,
 
727
                  const char *name,
 
728
                  const char **atts)
 
729
{
 
730
  svn_error_t *err;
 
731
  apr_pool_t *pool = loggy->pool; 
 
732
  int is_this_dir = (strcmp (name, SVN_WC_ENTRY_THIS_DIR) == 0);
 
733
  const char *rev = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_REVISION, atts);
 
734
  svn_boolean_t wc_root, overwrote_working = FALSE, remove_executable = FALSE;
 
735
  svn_boolean_t set_read_write = FALSE;
 
736
  const char *full_path;
 
737
  const char *pdir, *base_name;
 
738
  apr_hash_t *entries;
 
739
  const svn_wc_entry_t *orig_entry;
 
740
  svn_wc_entry_t *entry;
 
741
  apr_time_t text_time = 0; /* By default, don't override old stamp. */
 
742
  apr_time_t prop_time = 0; /* By default, don't override old stamp. */
 
743
  svn_node_kind_t kind;
 
744
  svn_wc_adm_access_t *adm_access;
 
745
 
 
746
  /* Determine the actual full path of the affected item. */
 
747
  if (! is_this_dir)
 
748
    full_path = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
 
749
                               name, pool);
 
750
  else
 
751
    full_path = apr_pstrdup (pool, svn_wc_adm_access_path (loggy->adm_access));
 
752
 
 
753
  /*** Perform sanity checking operations ***/
 
754
 
 
755
  /* If no new post-commit revision was given us, bail with an error. */
 
756
  if (! rev)
 
757
    return svn_error_createf (pick_error_code (loggy), NULL,
 
758
                              _("Missing 'revision' attribute for '%s'"),
 
759
                              name);
 
760
      
 
761
  /* Read the entry for the affected item.  If we can't find the
 
762
     entry, or if the entry states that our item is not either "this
 
763
     dir" or a file kind, perhaps this isn't really the entry our log
 
764
     creator was expecting.  */
 
765
  SVN_ERR (svn_wc_adm_probe_retrieve (&adm_access, loggy->adm_access, full_path,
 
766
                                      pool));
 
767
  SVN_ERR (svn_wc_entry (&orig_entry, full_path, adm_access, TRUE, pool));
 
768
  if ((! orig_entry)
 
769
      || ((! is_this_dir) && (orig_entry->kind != svn_node_file)))
 
770
    return svn_error_createf
 
771
      (pick_error_code (loggy), NULL,
 
772
       _("Log command for directory '%s' is mislocated"), name);
 
773
 
 
774
  entry = svn_wc_entry_dup (orig_entry, pool);
 
775
 
 
776
  /*** Handle the committed deletion case ***/
 
777
 
 
778
  /* If the committed item was scheduled for deletion, it needs to
 
779
     now be removed from revision control.  Once that is accomplished,
 
780
     we are finished handling this item.  */
 
781
  if (entry->schedule == svn_wc_schedule_delete)
 
782
    {
 
783
      svn_revnum_t new_rev = SVN_STR_TO_REV(rev);
 
784
 
 
785
      /* If we are suppose to delete "this dir", drop a 'killme' file
 
786
         into my own administrative dir as a signal for svn_wc__run_log() 
 
787
         to blow away the administrative area after it is finished
 
788
         processing this logfile.  */
 
789
      if (is_this_dir)
 
790
        {
 
791
          /* Bump the revision number of this_dir anyway, so that it
 
792
             might be higher than its parent's revnum.  If it's
 
793
             higher, then the process that sees KILLME and destroys
 
794
             the directory can also place a 'deleted' dir entry in the
 
795
             parent. */
 
796
          svn_wc_entry_t tmpentry;
 
797
          tmpentry.revision = new_rev;
 
798
          tmpentry.kind = svn_node_dir;
 
799
 
 
800
          SVN_ERR (svn_wc__entry_modify
 
801
                   (loggy->adm_access, NULL, &tmpentry,
 
802
                    SVN_WC__ENTRY_MODIFY_REVISION | SVN_WC__ENTRY_MODIFY_KIND,
 
803
                    FALSE, pool));
 
804
          loggy->entries_modified = TRUE;
 
805
 
 
806
          /* Drop the 'killme' file. */
 
807
          return svn_wc__make_adm_thing (loggy->adm_access, SVN_WC__ADM_KILLME,
 
808
                                         svn_node_file, APR_OS_DEFAULT,
 
809
                                         0, pool);
 
810
 
 
811
        }
 
812
 
 
813
      /* Else, we're deleting a file, and we can safely remove files
 
814
         from revision control without screwing something else up.
 
815
 
 
816
         ### We pass NULL, NULL for cancel_func and cancel_baton below.
 
817
         ### If they were available, it would be nice to use them. */
 
818
      else
 
819
        {         
 
820
          const svn_wc_entry_t *parentry;
 
821
          svn_wc_entry_t tmp_entry;
 
822
 
 
823
          SVN_ERR (svn_wc_remove_from_revision_control (loggy->adm_access,
 
824
                                                        name, FALSE, FALSE,
 
825
                                                        NULL, NULL,
 
826
                                                        pool));
 
827
          
 
828
          /* If the parent entry's working rev 'lags' behind new_rev... */
 
829
          SVN_ERR (svn_wc_entry (&parentry,
 
830
                                 svn_wc_adm_access_path (loggy->adm_access),
 
831
                                 loggy->adm_access,
 
832
                                 TRUE, pool));
 
833
          if (new_rev > parentry->revision)
 
834
            {
 
835
              /* ...then the parent's revision is now officially a
 
836
                 lie;  therefore, it must remember the file as being
 
837
                 'deleted' for a while.  Create a new, uninteresting
 
838
                 ghost entry:  */
 
839
              tmp_entry.kind = svn_node_file;
 
840
              tmp_entry.deleted = TRUE;
 
841
              tmp_entry.revision = new_rev;
 
842
              SVN_ERR (svn_wc__entry_modify
 
843
                       (loggy->adm_access, name, &tmp_entry,
 
844
                        SVN_WC__ENTRY_MODIFY_REVISION
 
845
                        | SVN_WC__ENTRY_MODIFY_KIND
 
846
                        | SVN_WC__ENTRY_MODIFY_DELETED,
 
847
                        FALSE, pool));
 
848
              loggy->entries_modified = TRUE;
 
849
            }
 
850
 
 
851
          return SVN_NO_ERROR;
 
852
        }
 
853
    }
 
854
 
 
855
 
 
856
  /*** Mark the committed item committed-to-date ***/
 
857
 
 
858
  
 
859
  /* If "this dir" has been replaced (delete + add), all its
 
860
     immmediate children *must* be either scheduled for deletion (they
 
861
     were children of "this dir" during the "delete" phase of its
 
862
     replacement), added (they are new children of the replaced dir),
 
863
     or replaced (they are new children of the replace dir that have
 
864
     the same names as children that were present during the "delete"
 
865
     phase of the replacement).  
 
866
 
 
867
     Children which are added or replaced will have been reported as
 
868
     individual commit targets, and thus will be re-visited by
 
869
     log_do_committed().  Children which were marked for deletion,
 
870
     however, need to be outright removed from revision control.  */
 
871
  if ((entry->schedule == svn_wc_schedule_replace) && is_this_dir)
 
872
    {
 
873
      apr_hash_index_t *hi;
 
874
              
 
875
      /* Loop over all children entries, look for items scheduled for
 
876
         deletion. */
 
877
      SVN_ERR (svn_wc_entries_read (&entries, loggy->adm_access, TRUE, pool));
 
878
      for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
 
879
        {
 
880
          const void *key;
 
881
          apr_ssize_t klen;
 
882
          void *val;
 
883
          const svn_wc_entry_t *cur_entry; 
 
884
          svn_wc_adm_access_t *entry_access;
 
885
                  
 
886
          /* Get the next entry */
 
887
          apr_hash_this (hi, &key, &klen, &val);
 
888
          cur_entry = (svn_wc_entry_t *) val;
 
889
                  
 
890
          /* Skip each entry that isn't scheduled for deletion. */
 
891
          if (cur_entry->schedule != svn_wc_schedule_delete)
 
892
            continue;
 
893
          
 
894
          /* Determine what arguments to hand to our removal function,
 
895
             and let BASE_NAME double as an "ok" flag to run that function. */
 
896
          base_name = NULL;
 
897
          if (cur_entry->kind == svn_node_file)
 
898
            {
 
899
              pdir = svn_wc_adm_access_path (loggy->adm_access);
 
900
              base_name = apr_pstrdup (pool, key);
 
901
              entry_access = loggy->adm_access;
 
902
            }
 
903
          else if (cur_entry->kind == svn_node_dir)
 
904
            {
 
905
              pdir = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
 
906
                                    key, pool);
 
907
              base_name = SVN_WC_ENTRY_THIS_DIR;
 
908
              SVN_ERR (svn_wc_adm_retrieve (&entry_access, loggy->adm_access,
 
909
                                            pdir, pool));
 
910
            }
 
911
 
 
912
          /* ### We pass NULL, NULL for cancel_func and cancel_baton below.
 
913
             ### If they were available, it would be nice to use them. */
 
914
          if (base_name)
 
915
            SVN_ERR (svn_wc_remove_from_revision_control 
 
916
                     (entry_access, base_name, FALSE, FALSE,
 
917
                      NULL, NULL, pool));
 
918
        }
 
919
    }
 
920
 
 
921
 
 
922
  /* For file commit items, we need to "install" the user's working
 
923
     file as the new `text-base' in the administrative area.  A copy
 
924
     of this file should have been dropped into our `tmp/text-base'
 
925
     directory during the commit process.  Part of this process
 
926
     involves setting the textual timestamp for this entry.  We'd like
 
927
     to just use the timestamp of the working file, but it is possible that
 
928
     at some point during the commit, the real working file might have
 
929
     changed again.  If that has happened, we'll use the timestamp of
 
930
     the copy of this file in `tmp/text-base'. */
 
931
  if (! is_this_dir)
 
932
    {
 
933
      const char *wf = full_path, *tmpf;
 
934
 
 
935
      /* Make sure our working file copy is present in the temp area. */
 
936
      tmpf = svn_wc__text_base_path (wf, 1, pool);
 
937
      if ((err = svn_io_check_path (tmpf, &kind, pool)))
 
938
        return svn_error_createf (pick_error_code (loggy), err,
 
939
                                  _("Error checking existence of '%s'"), name);
 
940
      if (kind == svn_node_file)
 
941
        {
 
942
          svn_boolean_t modified;
 
943
          const char *chosen;
 
944
 
 
945
          /* Verify that the working file is the same as the tmpf file. */
 
946
          if ((err = svn_wc__versioned_file_modcheck (&modified, wf,
 
947
                                                      loggy->adm_access,
 
948
                                                      tmpf, pool)))
 
949
            return svn_error_createf (pick_error_code (loggy), err,
 
950
                                      _("Error comparing '%s' and '%s'"),
 
951
                                      svn_path_local_style (wf, pool),
 
952
                                      svn_path_local_style (tmpf, pool));
 
953
 
 
954
          /* If they are the same, use the working file's timestamp,
 
955
             else use the tmpf file's timestamp. */
 
956
          chosen = modified ? tmpf : wf;
 
957
 
 
958
          /* Get the timestamp from our chosen file. */
 
959
          if ((err = svn_io_file_affected_time (&text_time, chosen, pool)))
 
960
            return svn_error_createf
 
961
              (pick_error_code (loggy), err,
 
962
               _("Error getting 'affected time' for '%s'"),
 
963
               svn_path_local_style (chosen, pool));
 
964
        }
 
965
    }
 
966
              
 
967
  /* Now check for property commits.  If a property commit occurred, a
 
968
     copy of the "working" property file should have been dumped in
 
969
     the admistrative `tmp' area.  We'll let that tmpfile's existence
 
970
     be a signal that we need to do post-commit property processing.
 
971
     Also, we have to again decide which timestamp to use (see the
 
972
     text-time case above).  */
 
973
  {
 
974
    const char *wf, *tmpf, *basef;
 
975
 
 
976
    /* Get property file pathnames (not from the `tmp' area) depending
 
977
       on whether we're examining a file or THIS_DIR */
 
978
    
 
979
    /* ### Logic check: if is_this_dir, then full_path is the same
 
980
       as loggy->adm_access->path, I think.  In which case we don't need the
 
981
       inline conditionals below... */
 
982
    
 
983
    SVN_ERR (svn_wc__prop_path
 
984
             (&wf,
 
985
              is_this_dir
 
986
              ? svn_wc_adm_access_path (loggy->adm_access) : full_path,
 
987
              loggy->adm_access, FALSE, pool));
 
988
    SVN_ERR (svn_wc__prop_base_path
 
989
             (&basef,
 
990
              is_this_dir
 
991
              ? svn_wc_adm_access_path (loggy->adm_access) : full_path,
 
992
              loggy->adm_access, FALSE, pool));
 
993
    
 
994
    /* If this file was replaced in the commit, then we definitely
 
995
       need to begin by removing any old residual prop-base file.  */
 
996
    if (entry->schedule == svn_wc_schedule_replace)
 
997
      {
 
998
        svn_node_kind_t kinder;
 
999
        SVN_ERR (svn_io_check_path (basef, &kinder, pool));
 
1000
        if (kinder == svn_node_file)
 
1001
          SVN_ERR (svn_io_remove_file (basef, pool));
 
1002
      }
 
1003
 
 
1004
    SVN_ERR (svn_wc__prop_path
 
1005
             (&tmpf,
 
1006
              is_this_dir
 
1007
              ? svn_wc_adm_access_path (loggy->adm_access) : full_path,
 
1008
              loggy->adm_access, TRUE, pool));
 
1009
    if ((err = svn_io_check_path (tmpf, &kind, pool)))
 
1010
      return svn_error_createf (pick_error_code (loggy), err,
 
1011
                                _("Error checking existence of '%s'"),
 
1012
                                svn_path_local_style (tmpf, pool));
 
1013
    if (kind == svn_node_file)
 
1014
      {
 
1015
        svn_boolean_t same;
 
1016
        const char *chosen;
 
1017
        
 
1018
        /* We need to decide which prop-timestamp to use, just like we
 
1019
           did with text-time above. */
 
1020
        if ((err = svn_io_files_contents_same_p (&same, wf, tmpf, pool)))
 
1021
          return svn_error_createf (pick_error_code (loggy), err,
 
1022
                                    _("Error comparing '%s' and '%s'"),
 
1023
                                    svn_path_local_style (wf, pool),
 
1024
                                    svn_path_local_style (tmpf, pool));
 
1025
 
 
1026
        /* If they are the same, use the working file's timestamp,
 
1027
           else use the tmp_base file's timestamp. */
 
1028
        chosen = same ? wf : tmpf;
 
1029
 
 
1030
        /* Get the timestamp of our chosen file. */
 
1031
        if ((err = svn_io_file_affected_time (&prop_time, chosen, pool)))
 
1032
          return svn_error_createf (pick_error_code (loggy), err,
 
1033
                                    _("Error getting 'affected time' of '%s'"),
 
1034
                                    svn_path_local_style (chosen, pool));
 
1035
 
 
1036
        /* Examine propchanges here before installing the new
 
1037
           propbase.  If the executable prop was -deleted-, then set a
 
1038
           flag that will remind us to run -x after our call to
 
1039
           install_committed_file(). */
 
1040
        if (! is_this_dir)
 
1041
          {
 
1042
            int i;
 
1043
            apr_array_header_t *propchanges;
 
1044
            SVN_ERR (svn_wc_get_prop_diffs (&propchanges, NULL,
 
1045
                                            full_path, loggy->adm_access,
 
1046
                                            pool));
 
1047
            for (i = 0; i < propchanges->nelts; i++)
 
1048
              {
 
1049
                svn_prop_t *propchange
 
1050
                  = &APR_ARRAY_IDX (propchanges, i, svn_prop_t);
 
1051
                
 
1052
                if ((! strcmp (propchange->name, SVN_PROP_EXECUTABLE))
 
1053
                    && (propchange->value == NULL))
 
1054
                  {
 
1055
                    remove_executable = TRUE;
 
1056
                    break;
 
1057
                  }
 
1058
              }                
 
1059
 
 
1060
            for (i = 0; i < propchanges->nelts; i++)
 
1061
              {
 
1062
                svn_prop_t *propchange
 
1063
                  = &APR_ARRAY_IDX (propchanges, i, svn_prop_t);
 
1064
                
 
1065
                if ((! strcmp (propchange->name, SVN_PROP_NEEDS_LOCK))
 
1066
                    && (propchange->value == NULL))
 
1067
                  {
 
1068
                    set_read_write = TRUE;
 
1069
                    break;
 
1070
                  }
 
1071
              }                
 
1072
          }
 
1073
 
 
1074
        /* Make the tmp prop file the new pristine one. */
 
1075
        SVN_ERR (svn_wc__prep_file_for_replacement (basef, TRUE, pool));
 
1076
        SVN_ERR (svn_io_file_rename (tmpf, basef, pool));
 
1077
        SVN_ERR (svn_io_set_file_read_only (basef, FALSE, pool));
 
1078
      }
 
1079
  }   
 
1080
 
 
1081
  /* Timestamps have been decided on, and prop-base has been installed
 
1082
     if necessary.  Now we install the new text-base (if present), and
 
1083
     possibly re-translate the working file. */
 
1084
  if (! is_this_dir)
 
1085
    {
 
1086
      /* Install the new file, which may involve expanding keywords. */
 
1087
      if ((err = install_committed_file
 
1088
           (&overwrote_working, loggy->adm_access, name, pool)))
 
1089
        return svn_error_createf
 
1090
          (pick_error_code (loggy), err,
 
1091
           _("Error replacing text-base of '%s'"), name);
 
1092
 
 
1093
      /* The previous call will have run +x if the executable property
 
1094
         was added or already present.  But if this property was
 
1095
         -removed-, (detected earlier), then run -x here on the new
 
1096
         working file.  */
 
1097
      if (remove_executable)
 
1098
        {
 
1099
          SVN_ERR (svn_io_set_file_executable (full_path,
 
1100
                                               FALSE, /* chmod -x */
 
1101
                                               FALSE, pool));
 
1102
          overwrote_working = TRUE; /* entry needs wc-file's timestamp  */
 
1103
        }
 
1104
 
 
1105
      if (set_read_write)
 
1106
        {
 
1107
          SVN_ERR (svn_io_set_file_read_write_carefully (full_path, TRUE, 
 
1108
                                                         FALSE, pool));
 
1109
          overwrote_working = TRUE; /* entry needs wc-file's timestamp  */
 
1110
        }
 
1111
 
 
1112
 
 
1113
      
 
1114
      /* If the working file was overwritten (due to re-translation)
 
1115
         or touched (due to +x / -x), then use *that* textual
 
1116
         timestamp instead. */
 
1117
      if (overwrote_working)
 
1118
        if ((err = svn_io_file_affected_time (&text_time, full_path, pool)))
 
1119
          return svn_error_createf (pick_error_code (loggy), err,
 
1120
                                    _("Error getting 'affected time' of '%s'"),
 
1121
                                    svn_path_local_style (full_path, pool));
 
1122
    }
 
1123
    
 
1124
  /* Files have been moved, and timestamps have been found.  It is now
 
1125
     fime for The Big Entry Modification. */
 
1126
  entry->revision = SVN_STR_TO_REV (rev);
 
1127
  entry->kind = is_this_dir ? svn_node_dir : svn_node_file;
 
1128
  entry->schedule = svn_wc_schedule_normal;
 
1129
  entry->copied = FALSE;
 
1130
  entry->deleted = FALSE;
 
1131
  entry->text_time = text_time;
 
1132
  entry->prop_time = prop_time;
 
1133
  entry->conflict_old = NULL;
 
1134
  entry->conflict_new = NULL;
 
1135
  entry->conflict_wrk = NULL;
 
1136
  entry->prejfile = NULL;
 
1137
  entry->copyfrom_url = NULL;
 
1138
  entry->copyfrom_rev = SVN_INVALID_REVNUM;
 
1139
  if ((err = svn_wc__entry_modify (loggy->adm_access, name, entry,
 
1140
                                   (SVN_WC__ENTRY_MODIFY_REVISION 
 
1141
                                    | SVN_WC__ENTRY_MODIFY_SCHEDULE 
 
1142
                                    | SVN_WC__ENTRY_MODIFY_COPIED
 
1143
                                    | SVN_WC__ENTRY_MODIFY_DELETED
 
1144
                                    | SVN_WC__ENTRY_MODIFY_COPYFROM_URL
 
1145
                                    | SVN_WC__ENTRY_MODIFY_COPYFROM_REV
 
1146
                                    | SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
 
1147
                                    | SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
 
1148
                                    | SVN_WC__ENTRY_MODIFY_CONFLICT_WRK
 
1149
                                    | SVN_WC__ENTRY_MODIFY_PREJFILE
 
1150
                                    | (text_time
 
1151
                                       ? SVN_WC__ENTRY_MODIFY_TEXT_TIME
 
1152
                                       : 0)
 
1153
                                    | (prop_time
 
1154
                                       ? SVN_WC__ENTRY_MODIFY_PROP_TIME
 
1155
                                       : 0)
 
1156
                                    | SVN_WC__ENTRY_MODIFY_FORCE),
 
1157
                                   FALSE, pool)))
 
1158
    return svn_error_createf
 
1159
      (pick_error_code (loggy), err,
 
1160
       _("Error modifying entry of '%s'"), name);
 
1161
  loggy->entries_modified = TRUE;
 
1162
 
 
1163
  /* If we aren't looking at "this dir" (meaning we are looking at a
 
1164
     file), we are finished.  From here on out, it's all about a
 
1165
     directory's entry in its parent.  */
 
1166
  if (! is_this_dir)
 
1167
    return SVN_NO_ERROR;
 
1168
 
 
1169
  /* For directories, we also have to reset the state in the parent's
 
1170
     entry for this directory, unless the current directory is a `WC
 
1171
     root' (meaning, our parent directory on disk is not our parent in
 
1172
     Version Control Land), in which case we're all finished here. */
 
1173
  SVN_ERR (svn_wc_is_wc_root (&wc_root,
 
1174
                              svn_wc_adm_access_path (loggy->adm_access),
 
1175
                              loggy->adm_access,
 
1176
                              pool));
 
1177
  if (wc_root)
 
1178
    return SVN_NO_ERROR;
 
1179
 
 
1180
  /* Make sure our entry exists in the parent. */
 
1181
  {
 
1182
    svn_wc_adm_access_t *paccess;
 
1183
    svn_boolean_t unassociated = FALSE;
 
1184
    
 
1185
    svn_path_split (svn_wc_adm_access_path (loggy->adm_access), &pdir,
 
1186
                    &base_name, pool);
 
1187
    
 
1188
    err = svn_wc_adm_retrieve (&paccess, loggy->adm_access, pdir, pool);
 
1189
    if (err && (err->apr_err == SVN_ERR_WC_NOT_LOCKED))
 
1190
      {
 
1191
        svn_error_clear (err);
 
1192
        SVN_ERR (svn_wc_adm_open3 (&paccess, NULL, pdir, TRUE, 0,
 
1193
                                   NULL, NULL, pool));
 
1194
        unassociated = TRUE;
 
1195
      }
 
1196
    else if (err)
 
1197
      return err;
 
1198
    
 
1199
    SVN_ERR (svn_wc_entries_read (&entries, paccess, FALSE, pool));
 
1200
    if (apr_hash_get (entries, base_name, APR_HASH_KEY_STRING))
 
1201
      {
 
1202
        if ((err = svn_wc__entry_modify (paccess, base_name, entry,
 
1203
                                         (SVN_WC__ENTRY_MODIFY_SCHEDULE 
 
1204
                                          | SVN_WC__ENTRY_MODIFY_COPIED
 
1205
                                          | SVN_WC__ENTRY_MODIFY_DELETED
 
1206
                                          | SVN_WC__ENTRY_MODIFY_FORCE),
 
1207
                                         TRUE, pool)))
 
1208
          return svn_error_createf (pick_error_code (loggy), err,
 
1209
                                    _("Error modifying entry of '%s'"), name);
 
1210
      }
 
1211
 
 
1212
    if (unassociated)
 
1213
      SVN_ERR (svn_wc_adm_close (paccess));
 
1214
  }
 
1215
 
 
1216
  return SVN_NO_ERROR;
 
1217
}
 
1218
 
 
1219
 
 
1220
/* See documentation for SVN_WC__LOG_MODIFY_WCPROP. */
 
1221
static svn_error_t *
 
1222
log_do_modify_wcprop (struct log_runner *loggy,
 
1223
                      const char *name,
 
1224
                      const char **atts)
 
1225
{
 
1226
  svn_string_t value;
 
1227
  const char *propname, *propval, *path; 
 
1228
 
 
1229
  if (strcmp (name, SVN_WC_ENTRY_THIS_DIR) == 0)
 
1230
    path = svn_wc_adm_access_path (loggy->adm_access);
 
1231
  else
 
1232
    path = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
 
1233
                          name, loggy->pool);
 
1234
 
 
1235
  propname = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_PROPNAME, atts);
 
1236
  propval = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_PROPVAL, atts);
 
1237
 
 
1238
  if (propval)
 
1239
    {
 
1240
      value.data = propval;
 
1241
      value.len = strlen (propval);
 
1242
    }
 
1243
 
 
1244
  return svn_wc__wcprop_set (propname, propval ? &value : NULL,
 
1245
                             path, loggy->adm_access, loggy->pool);
 
1246
}
 
1247
 
 
1248
 
 
1249
static void
 
1250
start_handler (void *userData, const char *eltname, const char **atts)
 
1251
{
 
1252
  svn_error_t *err = SVN_NO_ERROR;
 
1253
  struct log_runner *loggy = userData;
 
1254
 
 
1255
  /* All elements use the `name' attribute, so grab it now. */
 
1256
  const char *name = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_NAME, atts);
 
1257
 
 
1258
  /* Clear the per-log-item pool. */
 
1259
  svn_pool_clear (loggy->pool);
 
1260
 
 
1261
  if (strcmp (eltname, "wc-log") == 0)   /* ignore expat pacifier */
 
1262
    return;
 
1263
  else if (! name)
 
1264
    {
 
1265
      signal_error
 
1266
        (loggy, svn_error_createf 
 
1267
         (pick_error_code (loggy), NULL,
 
1268
          _("Log entry missing 'name' attribute (entry '%s' "
 
1269
            "for directory '%s')"),
 
1270
          eltname,
 
1271
          svn_path_local_style (svn_wc_adm_access_path (loggy->adm_access),
 
1272
                                loggy->pool)));
 
1273
      return;
 
1274
    }
 
1275
  
 
1276
  /* Increment the top-level element count before processing any commands. */
 
1277
  loggy->count += 1;
 
1278
 
 
1279
  /* Dispatch. */
 
1280
  if (strcmp (eltname, SVN_WC__LOG_MODIFY_ENTRY) == 0) {
 
1281
    err = log_do_modify_entry (loggy, name, atts);
 
1282
  }
 
1283
  else if (strcmp (eltname, SVN_WC__LOG_DELETE_LOCK) == 0) {
 
1284
    err = log_do_delete_lock (loggy, name);
 
1285
  }
 
1286
  else if (strcmp (eltname, SVN_WC__LOG_DELETE_ENTRY) == 0) {
 
1287
    err = log_do_delete_entry (loggy, name);
 
1288
  }
 
1289
  else if (strcmp (eltname, SVN_WC__LOG_COMMITTED) == 0) {
 
1290
    err = log_do_committed (loggy, name, atts);
 
1291
  }
 
1292
  else if (strcmp (eltname, SVN_WC__LOG_MODIFY_WCPROP) == 0) {
 
1293
    err = log_do_modify_wcprop (loggy, name, atts);
 
1294
  }
 
1295
  else if (strcmp (eltname, SVN_WC__LOG_RM) == 0) {
 
1296
    err = log_do_rm (loggy, name);
 
1297
  }
 
1298
  else if (strcmp (eltname, SVN_WC__LOG_MERGE) == 0) {
 
1299
    err = log_do_merge (loggy, name, atts);
 
1300
  }
 
1301
  else if (strcmp (eltname, SVN_WC__LOG_MV) == 0) {
 
1302
    err = log_do_file_xfer (loggy, name, svn_wc__xfer_mv, atts);
 
1303
  }
 
1304
  else if (strcmp (eltname, SVN_WC__LOG_CP) == 0) {
 
1305
    err = log_do_file_xfer (loggy, name, svn_wc__xfer_cp, atts);
 
1306
  }
 
1307
  else if (strcmp (eltname, SVN_WC__LOG_CP_AND_TRANSLATE) == 0) {
 
1308
    err = log_do_file_xfer (loggy, name,svn_wc__xfer_cp_and_translate, atts);
 
1309
  }
 
1310
  else if (strcmp (eltname, SVN_WC__LOG_CP_AND_DETRANSLATE) == 0) {
 
1311
    err = log_do_file_xfer (loggy, name,svn_wc__xfer_cp_and_detranslate, atts);
 
1312
  }
 
1313
  else if (strcmp (eltname, SVN_WC__LOG_APPEND) == 0) {
 
1314
    err = log_do_file_xfer (loggy, name, svn_wc__xfer_append, atts);
 
1315
  }
 
1316
  else if (strcmp (eltname, SVN_WC__LOG_READONLY) == 0) {
 
1317
    err = log_do_file_readonly (loggy, name);
 
1318
  }
 
1319
  else if (strcmp (eltname, SVN_WC__LOG_MAYBE_READONLY) == 0) {
 
1320
    err = log_do_file_maybe_readonly (loggy, name);
 
1321
  }
 
1322
  else if (strcmp (eltname, SVN_WC__LOG_SET_TIMESTAMP) == 0) {
 
1323
    err = log_do_file_timestamp (loggy, name, atts);
 
1324
  }
 
1325
  else
 
1326
    {
 
1327
      signal_error
 
1328
        (loggy, svn_error_createf
 
1329
         (pick_error_code (loggy), NULL,
 
1330
          _("Unrecognized logfile element '%s' in '%s'"),
 
1331
          eltname,
 
1332
          svn_path_local_style (svn_wc_adm_access_path (loggy->adm_access),
 
1333
                                loggy->pool)));
 
1334
      return;
 
1335
    }
 
1336
 
 
1337
  if (err)
 
1338
    signal_error
 
1339
      (loggy, svn_error_createf
 
1340
       (pick_error_code (loggy), err,
 
1341
        _("Error processing command '%s' in '%s'"),
 
1342
        eltname,
 
1343
        svn_path_local_style (svn_wc_adm_access_path (loggy->adm_access),
 
1344
                              loggy->pool)));
 
1345
  
 
1346
  return;
 
1347
}
 
1348
 
 
1349
/* Process the "KILLME" file in ADM_ACCESS
 
1350
 */
 
1351
static svn_error_t *
 
1352
handle_killme (svn_wc_adm_access_t *adm_access,
 
1353
               svn_cancel_func_t cancel_func,
 
1354
               void *cancel_baton,
 
1355
               apr_pool_t *pool)
 
1356
{
 
1357
  const svn_wc_entry_t *thisdir_entry, *parent_entry;
 
1358
  svn_wc_entry_t tmp_entry;
 
1359
  svn_error_t *err;
 
1360
  SVN_ERR (svn_wc_entry (&thisdir_entry,
 
1361
                         svn_wc_adm_access_path (adm_access), adm_access,
 
1362
                         FALSE, pool));
 
1363
 
 
1364
  /* Blow away the entire directory, and all those below it too. */
 
1365
  err = svn_wc_remove_from_revision_control (adm_access,
 
1366
                                             SVN_WC_ENTRY_THIS_DIR,
 
1367
                                             TRUE, /* destroy */
 
1368
                                             FALSE, /* no instant err */
 
1369
                                             cancel_func, cancel_baton,
 
1370
                                             pool);
 
1371
  if (err && err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
 
1372
    return err;
 
1373
  svn_error_clear (err);
 
1374
 
 
1375
  /* If revnum of this dir is greater than parent's revnum, then
 
1376
     recreate 'deleted' entry in parent. */
 
1377
  {
 
1378
    const char *parent, *bname;
 
1379
    svn_wc_adm_access_t *parent_access;
 
1380
 
 
1381
    svn_path_split (svn_wc_adm_access_path (adm_access), &parent, &bname, pool);
 
1382
    SVN_ERR (svn_wc_adm_retrieve (&parent_access, adm_access, parent, pool));
 
1383
    SVN_ERR (svn_wc_entry (&parent_entry, parent, parent_access, FALSE, pool));
 
1384
        
 
1385
    if (thisdir_entry->revision > parent_entry->revision)
 
1386
      {
 
1387
        tmp_entry.kind = svn_node_dir;
 
1388
        tmp_entry.deleted = TRUE;
 
1389
        tmp_entry.revision = thisdir_entry->revision;
 
1390
        SVN_ERR (svn_wc__entry_modify (parent_access, bname, &tmp_entry,
 
1391
                                       SVN_WC__ENTRY_MODIFY_REVISION
 
1392
                                       | SVN_WC__ENTRY_MODIFY_KIND
 
1393
                                       | SVN_WC__ENTRY_MODIFY_DELETED,
 
1394
                                       TRUE, pool));            
 
1395
      }
 
1396
  }
 
1397
  return SVN_NO_ERROR;
 
1398
}
 
1399
 
 
1400
 
 
1401
/*** Using the parser to run the log file. ***/
 
1402
 
 
1403
/* Determine the log file that should be used for a given number. */
 
1404
const char *
 
1405
svn_wc__logfile_path (int log_number,
 
1406
                      apr_pool_t *pool)
 
1407
{
 
1408
  return apr_psprintf (pool, SVN_WC__ADM_LOG "%s",
 
1409
                       (log_number == 0) ? ""
 
1410
                       : apr_psprintf (pool, ".%d", log_number));
 
1411
}
 
1412
 
 
1413
/* Run a sequence of log files. */
 
1414
svn_error_t *
 
1415
svn_wc__run_log (svn_wc_adm_access_t *adm_access,
 
1416
                 const char *diff3_cmd,
 
1417
                 apr_pool_t *pool)
 
1418
{
 
1419
  svn_error_t *err, *err2;
 
1420
  svn_xml_parser_t *parser;
 
1421
  struct log_runner *loggy = apr_pcalloc (pool, sizeof (*loggy));
 
1422
  char buf[BUFSIZ];
 
1423
  apr_size_t buf_len;
 
1424
  apr_file_t *f = NULL;
 
1425
  const char *logfile_path;
 
1426
  int log_number;
 
1427
  apr_pool_t *iterpool = svn_pool_create (pool);
 
1428
 
 
1429
  /* kff todo: use the tag-making functions here, now. */
 
1430
  const char *log_start
 
1431
    = "<wc-log xmlns=\"http://subversion.tigris.org/xmlns\">\n";
 
1432
  const char *log_end
 
1433
    = "</wc-log>\n";
 
1434
 
 
1435
  parser = svn_xml_make_parser (loggy, start_handler, NULL, NULL, pool);
 
1436
  loggy->adm_access = adm_access;
 
1437
  loggy->pool = svn_pool_create (pool);
 
1438
  loggy->parser = parser;
 
1439
  loggy->entries_modified = FALSE;
 
1440
  loggy->diff3_cmd = diff3_cmd;
 
1441
  loggy->count = 0;
 
1442
 
 
1443
  /* Expat wants everything wrapped in a top-level form, so start with
 
1444
     a ghost open tag. */
 
1445
  SVN_ERR (svn_xml_parse (parser, log_start, strlen (log_start), 0));
 
1446
 
 
1447
  for (log_number = 0; ; log_number++)
 
1448
    {
 
1449
      svn_pool_clear (iterpool);
 
1450
      logfile_path = svn_wc__logfile_path (log_number, iterpool);
 
1451
      /* Parse the log file's contents. */
 
1452
      err = svn_wc__open_adm_file (&f, svn_wc_adm_access_path (adm_access),
 
1453
                                   logfile_path, APR_READ, iterpool);
 
1454
      if (err)
 
1455
        {
 
1456
          if (APR_STATUS_IS_ENOENT (err->apr_err))
 
1457
            {
 
1458
              svn_error_clear (err);
 
1459
              break;
 
1460
            }
 
1461
          else
 
1462
            {
 
1463
              SVN_ERR_W (err, _("Couldn't open log"));
 
1464
            }
 
1465
        }
 
1466
      
 
1467
      do {
 
1468
        buf_len = sizeof (buf);
 
1469
        
 
1470
        err = svn_io_file_read (f, buf, &buf_len, iterpool);
 
1471
        if (err && !APR_STATUS_IS_EOF(err->apr_err))
 
1472
          return svn_error_createf
 
1473
            (err->apr_err, err,
 
1474
             _("Error reading administrative log file in '%s'"),
 
1475
             svn_path_local_style (svn_wc_adm_access_path (adm_access),
 
1476
                                   iterpool));
 
1477
        
 
1478
        err2 = svn_xml_parse (parser, buf, buf_len, 0);
 
1479
        if (err2)
 
1480
          {
 
1481
            if (err)
 
1482
              svn_error_clear (err);
 
1483
            SVN_ERR (err2);
 
1484
          }
 
1485
      } while (! err);
 
1486
      
 
1487
      svn_error_clear (err);
 
1488
      SVN_ERR (svn_io_file_close (f, iterpool));
 
1489
    }
 
1490
 
 
1491
 
 
1492
  /* Pacify Expat with a pointless closing element tag. */
 
1493
  SVN_ERR (svn_xml_parse (parser, log_end, strlen (log_end), 1));
 
1494
 
 
1495
  svn_xml_free_parser (parser);
 
1496
 
 
1497
  if (loggy->entries_modified == TRUE)
 
1498
    {
 
1499
      apr_hash_t *entries;
 
1500
      SVN_ERR (svn_wc_entries_read (&entries, loggy->adm_access, TRUE, pool));
 
1501
      SVN_ERR (svn_wc__entries_write (entries, loggy->adm_access, pool));
 
1502
    }
 
1503
 
 
1504
  /* Check for a 'killme' file in the administrative area. */
 
1505
  if (svn_wc__adm_path_exists (svn_wc_adm_access_path (adm_access), 0, pool,
 
1506
                               SVN_WC__ADM_KILLME, NULL))
 
1507
    {
 
1508
      SVN_ERR (handle_killme (adm_access, NULL, NULL, pool));
 
1509
    }
 
1510
  else
 
1511
    {
 
1512
      for (log_number--; log_number >= 0; log_number--)
 
1513
        {
 
1514
          svn_pool_clear (iterpool);
 
1515
          logfile_path = svn_wc__logfile_path (log_number, iterpool);
 
1516
          
 
1517
          /* No 'killme'?  Remove the logfile;  its commands have been executed. */
 
1518
          SVN_ERR (svn_wc__remove_adm_file (svn_wc_adm_access_path (adm_access),
 
1519
                                            iterpool, logfile_path, NULL));
 
1520
        }
 
1521
    }
 
1522
 
 
1523
  svn_pool_destroy (iterpool);
 
1524
 
 
1525
  return SVN_NO_ERROR;
 
1526
}
 
1527
 
 
1528
 
 
1529
 
 
1530
/*** Recursively do log things. ***/
 
1531
 
 
1532
svn_error_t *
 
1533
svn_wc_cleanup (const char *path,
 
1534
                svn_wc_adm_access_t *optional_adm_access,
 
1535
                const char *diff3_cmd,
 
1536
                svn_cancel_func_t cancel_func,
 
1537
                void *cancel_baton,
 
1538
                apr_pool_t *pool)
 
1539
{
 
1540
  return svn_wc_cleanup2 (path, diff3_cmd, cancel_func, cancel_baton, pool);
 
1541
}
 
1542
 
 
1543
svn_error_t *
 
1544
svn_wc_cleanup2 (const char *path,
 
1545
                 const char *diff3_cmd,
 
1546
                 svn_cancel_func_t cancel_func,
 
1547
                 void *cancel_baton,
 
1548
                 apr_pool_t *pool)
 
1549
{
 
1550
  apr_hash_t *entries = NULL;
 
1551
  apr_hash_index_t *hi;
 
1552
  svn_node_kind_t kind;
 
1553
  svn_wc_adm_access_t *adm_access;
 
1554
  svn_boolean_t cleanup;
 
1555
  int wc_format_version;
 
1556
  apr_pool_t *subpool;
 
1557
 
 
1558
  /* Check cancellation; note that this catches recursive calls too. */
 
1559
  if (cancel_func)
 
1560
    SVN_ERR (cancel_func (cancel_baton));
 
1561
 
 
1562
  SVN_ERR (svn_wc_check_wc (path, &wc_format_version, pool));
 
1563
 
 
1564
  /* a "version" of 0 means a non-wc directory */
 
1565
  if (wc_format_version == 0)
 
1566
    return svn_error_createf
 
1567
      (SVN_ERR_WC_NOT_DIRECTORY, NULL,
 
1568
       _("'%s' is not a working copy directory"),
 
1569
       svn_path_local_style (path, pool));
 
1570
 
 
1571
  /* Lock this working copy directory, or steal an existing lock */
 
1572
  SVN_ERR (svn_wc__adm_steal_write_lock (&adm_access, NULL, path, pool));
 
1573
 
 
1574
  /* Recurse on versioned elements first, oddly enough. */
 
1575
  SVN_ERR (svn_wc_entries_read (&entries, adm_access, FALSE, pool));
 
1576
  subpool = svn_pool_create (pool);
 
1577
  for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
 
1578
    {
 
1579
      const void *key;
 
1580
      void *val;
 
1581
      const svn_wc_entry_t *entry;
 
1582
      const char *entry_path;
 
1583
 
 
1584
      svn_pool_clear (subpool);
 
1585
      apr_hash_this (hi, &key, NULL, &val);
 
1586
      entry = val;
 
1587
      entry_path = svn_path_join (path, key, subpool);
 
1588
 
 
1589
      if (entry->kind == svn_node_dir
 
1590
          && strcmp (key, SVN_WC_ENTRY_THIS_DIR) != 0)
 
1591
        {
 
1592
          /* Sub-directories */
 
1593
          SVN_ERR (svn_io_check_path (entry_path, &kind, subpool));
 
1594
          if (kind == svn_node_dir)
 
1595
            SVN_ERR (svn_wc_cleanup2 (entry_path, diff3_cmd,
 
1596
                                      cancel_func, cancel_baton, subpool));
 
1597
        }
 
1598
      else
 
1599
        {
 
1600
          /* "." and things that are not directories, check for mods to
 
1601
             trigger the timestamp repair mechanism.  Since this rewrites
 
1602
             the entries file for each timestamp fixed it has the potential
 
1603
             to be slow, perhaps we need something more sophisticated? */
 
1604
          svn_boolean_t modified;
 
1605
          SVN_ERR (svn_wc_props_modified_p (&modified, entry_path,
 
1606
                                            adm_access, subpool));
 
1607
          if (entry->kind == svn_node_file)
 
1608
            SVN_ERR (svn_wc_text_modified_p (&modified, entry_path, FALSE,
 
1609
                                             adm_access, subpool));
 
1610
        }
 
1611
    }
 
1612
  svn_pool_destroy (subpool);
 
1613
 
 
1614
  if (svn_wc__adm_path_exists (svn_wc_adm_access_path (adm_access), 0, pool,
 
1615
                               SVN_WC__ADM_KILLME, NULL))
 
1616
    {
 
1617
      /* A KILLME indicates that the log has already been run */
 
1618
      SVN_ERR (handle_killme (adm_access, cancel_func, cancel_baton, pool));
 
1619
    }
 
1620
  else
 
1621
    {
 
1622
      /* In an attempt to maintain consitency between the decisions made in
 
1623
         this function, and those made in the access baton lock-removal code,
 
1624
         we use the same test as the lock-removal code. */
 
1625
      SVN_ERR (svn_wc__adm_is_cleanup_required (&cleanup, adm_access, pool));
 
1626
      if (cleanup)
 
1627
        SVN_ERR (svn_wc__run_log (adm_access, diff3_cmd, pool));
 
1628
    }
 
1629
 
 
1630
  /* Cleanup the tmp area of the admin subdir, if running the log has not
 
1631
     removed it!  The logs have been run, so anything left here has no hope
 
1632
     of being useful. */
 
1633
  if (svn_wc__adm_path_exists (path, 0, pool, NULL))
 
1634
    SVN_ERR (svn_wc__adm_cleanup_tmp_area (adm_access, pool));
 
1635
 
 
1636
  SVN_ERR (svn_wc_adm_close (adm_access));
 
1637
 
 
1638
  return SVN_NO_ERROR;
 
1639
}