1/*
2 * node.c:  routines for getting information about nodes in the working copy.
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24/* A note about these functions:
25
26   We aren't really sure yet which bits of data libsvn_client needs about
27   nodes.  In wc-1, we just grab the entry, and then use whatever we want
28   from it.  Such a pattern is Bad.
29
30   This file is intended to hold functions which retrieve specific bits of
31   information about a node, and will hopefully give us a better idea about
32   what data libsvn_client needs, and how to best provide that data in 1.7
33   final.  As such, these functions should only be called from outside
34   libsvn_wc; any internal callers are encouraged to use the appropriate
35   information fetching function, such as svn_wc__db_read_info().
36*/
37
38#include <apr_pools.h>
39#include <apr_time.h>
40
41#include "svn_pools.h"
42#include "svn_dirent_uri.h"
43#include "svn_path.h"
44#include "svn_hash.h"
45#include "svn_types.h"
46
47#include "wc.h"
48#include "props.h"
49#include "entries.h"
50#include "wc_db.h"
51
52#include "svn_private_config.h"
53#include "private/svn_wc_private.h"
54
55
56/* Set *CHILDREN_ABSPATHS to a new array of the full paths formed by joining
57 * each name in REL_CHILDREN onto DIR_ABSPATH.  If SHOW_HIDDEN is false then
58 * omit any paths that are reported as 'hidden' by svn_wc__db_node_hidden().
59 *
60 * Allocate the output array and its elements in RESULT_POOL. */
61static svn_error_t *
62filter_and_make_absolute(const apr_array_header_t **children_abspaths,
63                         svn_wc_context_t *wc_ctx,
64                         const char *dir_abspath,
65                         const apr_array_header_t *rel_children,
66                         svn_boolean_t show_hidden,
67                         apr_pool_t *result_pool,
68                         apr_pool_t *scratch_pool)
69{
70  apr_array_header_t *children;
71  int i;
72
73  children = apr_array_make(result_pool, rel_children->nelts,
74                            sizeof(const char *));
75  for (i = 0; i < rel_children->nelts; i++)
76    {
77      const char *child_abspath = svn_dirent_join(dir_abspath,
78                                                  APR_ARRAY_IDX(rel_children,
79                                                                i,
80                                                                const char *),
81                                                  result_pool);
82
83      /* Don't add hidden nodes to *CHILDREN if we don't want them. */
84      if (!show_hidden)
85        {
86          svn_boolean_t child_is_hidden;
87
88          SVN_ERR(svn_wc__db_node_hidden(&child_is_hidden, wc_ctx->db,
89                                         child_abspath, scratch_pool));
90          if (child_is_hidden)
91            continue;
92        }
93
94      APR_ARRAY_PUSH(children, const char *) = child_abspath;
95    }
96
97  *children_abspaths = children;
98
99  return SVN_NO_ERROR;
100}
101
102
103svn_error_t *
104svn_wc__node_get_children_of_working_node(const apr_array_header_t **children,
105                                          svn_wc_context_t *wc_ctx,
106                                          const char *dir_abspath,
107                                          svn_boolean_t show_hidden,
108                                          apr_pool_t *result_pool,
109                                          apr_pool_t *scratch_pool)
110{
111  const apr_array_header_t *rel_children;
112
113  SVN_ERR(svn_wc__db_read_children_of_working_node(&rel_children,
114                                                   wc_ctx->db, dir_abspath,
115                                                   scratch_pool, scratch_pool));
116  SVN_ERR(filter_and_make_absolute(children, wc_ctx, dir_abspath,
117                                   rel_children, show_hidden,
118                                   result_pool, scratch_pool));
119  return SVN_NO_ERROR;
120}
121
122svn_error_t *
123svn_wc__node_get_children(const apr_array_header_t **children,
124                          svn_wc_context_t *wc_ctx,
125                          const char *dir_abspath,
126                          svn_boolean_t show_hidden,
127                          apr_pool_t *result_pool,
128                          apr_pool_t *scratch_pool)
129{
130  const apr_array_header_t *rel_children;
131
132  SVN_ERR(svn_wc__db_read_children(&rel_children, wc_ctx->db, dir_abspath,
133                                   scratch_pool, scratch_pool));
134  SVN_ERR(filter_and_make_absolute(children, wc_ctx, dir_abspath,
135                                   rel_children, show_hidden,
136                                   result_pool, scratch_pool));
137  return SVN_NO_ERROR;
138}
139
140
141svn_error_t *
142svn_wc__internal_get_repos_info(svn_revnum_t *revision,
143                                const char **repos_relpath,
144                                const char **repos_root_url,
145                                const char **repos_uuid,
146                                svn_wc__db_t *db,
147                                const char *local_abspath,
148                                apr_pool_t *result_pool,
149                                apr_pool_t *scratch_pool)
150{
151  svn_wc__db_status_t status;
152  svn_boolean_t have_work;
153
154  SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath,
155                               repos_root_url, repos_uuid,
156                               NULL, NULL, NULL, NULL, NULL, NULL,
157                               NULL, NULL, NULL, NULL, NULL, NULL,
158                               NULL, NULL, NULL, NULL, NULL, NULL,
159                               NULL, NULL, &have_work,
160                               db, local_abspath,
161                               result_pool, scratch_pool));
162
163  if ((repos_relpath ? *repos_relpath != NULL : TRUE)
164      && (repos_root_url ? *repos_root_url  != NULL: TRUE)
165      && (repos_uuid ? *repos_uuid != NULL : TRUE))
166    return SVN_NO_ERROR; /* We got the requested information */
167
168  if (!have_work) /* not-present, (server-)excluded? */
169    {
170      return SVN_NO_ERROR; /* Can't fetch more */
171    }
172
173  if (status == svn_wc__db_status_deleted)
174    {
175      const char *base_del_abspath, *wrk_del_abspath;
176
177      SVN_ERR(svn_wc__db_scan_deletion(&base_del_abspath, NULL,
178                                       &wrk_del_abspath, NULL,
179                                       db, local_abspath,
180                                       scratch_pool, scratch_pool));
181
182      if (base_del_abspath)
183        {
184          SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, repos_relpath,
185                                           repos_root_url, repos_uuid, NULL,
186                                           NULL, NULL, NULL, NULL, NULL, NULL,
187                                           NULL, NULL, NULL,
188                                           db, base_del_abspath,
189                                           result_pool, scratch_pool));
190
191          /* If we have a repos_relpath, it is of the op-root */
192          if (repos_relpath)
193            *repos_relpath = svn_relpath_join(*repos_relpath,
194                                svn_dirent_skip_ancestor(base_del_abspath,
195                                                         local_abspath),
196                                              result_pool);
197          /* We keep revision as SVN_INVALID_REVNUM */
198        }
199      else if (wrk_del_abspath)
200        {
201          const char *op_root_abspath = NULL;
202
203          SVN_ERR(svn_wc__db_scan_addition(NULL, repos_relpath
204                                                    ? &op_root_abspath : NULL,
205                                           repos_relpath, repos_root_url,
206                                           repos_uuid, NULL, NULL, NULL, NULL,
207                                           db, svn_dirent_dirname(
208                                                   wrk_del_abspath,
209                                                   scratch_pool),
210                                           result_pool, scratch_pool));
211
212          /* If we have a repos_relpath, it is of the op-root */
213          if (repos_relpath)
214            *repos_relpath = svn_relpath_join(
215                                    *repos_relpath,
216                                    svn_dirent_skip_ancestor(op_root_abspath,
217                                                             local_abspath),
218                                    result_pool);
219        }
220    }
221  else /* added, or WORKING incomplete */
222    {
223      const char *op_root_abspath = NULL;
224
225      /* We have an addition. scan_addition() will find the intended
226         repository location by scanning up the tree.  */
227      SVN_ERR(svn_wc__db_scan_addition(NULL, repos_relpath
228                                                    ? &op_root_abspath : NULL,
229                                       repos_relpath, repos_root_url,
230                                       repos_uuid, NULL, NULL, NULL, NULL,
231                                       db, local_abspath,
232                                       result_pool, scratch_pool));
233    }
234
235  SVN_ERR_ASSERT(repos_root_url == NULL || *repos_root_url != NULL);
236  SVN_ERR_ASSERT(repos_uuid == NULL || *repos_uuid != NULL);
237  return SVN_NO_ERROR;
238}
239
240svn_error_t *
241svn_wc__node_get_repos_info(svn_revnum_t *revision,
242                            const char **repos_relpath,
243                            const char **repos_root_url,
244                            const char **repos_uuid,
245                            svn_wc_context_t *wc_ctx,
246                            const char *local_abspath,
247                            apr_pool_t *result_pool,
248                            apr_pool_t *scratch_pool)
249{
250  return svn_error_trace(
251            svn_wc__internal_get_repos_info(revision,
252                                            repos_relpath,
253                                            repos_root_url,
254                                            repos_uuid,
255                                            wc_ctx->db, local_abspath,
256                                            result_pool, scratch_pool));
257}
258
259/* Convert DB_KIND into the appropriate NODE_KIND value.
260 * If SHOW_HIDDEN is TRUE, report the node kind as found in the DB
261 * even if DB_STATUS indicates that the node is hidden.
262 * Else, return svn_node_none for such nodes.
263 *
264 * ### This is a bit ugly. We should consider promoting svn_kind_t
265 * ### to the de-facto node kind type instead of converting between them
266 * ### in non-backwards compat code.
267 * ### See also comments at the definition of svn_kind_t.
268 *
269 * ### In reality, the previous comment is out of date, as there is
270 * ### now only one enumeration for node kinds, and that is
271 * ### svn_node_kind_t (svn_kind_t was merged with that). But it's
272 * ### still ugly.
273 */
274static svn_error_t *
275convert_db_kind_to_node_kind(svn_node_kind_t *node_kind,
276                             svn_node_kind_t db_kind,
277                             svn_wc__db_status_t db_status,
278                             svn_boolean_t show_hidden)
279{
280  *node_kind = db_kind;
281
282  /* Make sure hidden nodes return svn_node_none. */
283  if (! show_hidden)
284    switch (db_status)
285      {
286        case svn_wc__db_status_not_present:
287        case svn_wc__db_status_server_excluded:
288        case svn_wc__db_status_excluded:
289          *node_kind = svn_node_none;
290
291        default:
292          break;
293      }
294
295  return SVN_NO_ERROR;
296}
297
298svn_error_t *
299svn_wc_read_kind2(svn_node_kind_t *kind,
300                  svn_wc_context_t *wc_ctx,
301                  const char *local_abspath,
302                  svn_boolean_t show_deleted,
303                  svn_boolean_t show_hidden,
304                  apr_pool_t *scratch_pool)
305{
306  svn_node_kind_t db_kind;
307
308  SVN_ERR(svn_wc__db_read_kind(&db_kind,
309                               wc_ctx->db, local_abspath,
310                               TRUE,
311                               show_deleted,
312                               show_hidden,
313                               scratch_pool));
314
315  if (db_kind == svn_node_dir)
316    *kind = svn_node_dir;
317  else if (db_kind == svn_node_file || db_kind == svn_node_symlink)
318    *kind = svn_node_file;
319  else
320    *kind = svn_node_none;
321
322  return SVN_NO_ERROR;
323}
324
325svn_error_t *
326svn_wc__node_get_depth(svn_depth_t *depth,
327                       svn_wc_context_t *wc_ctx,
328                       const char *local_abspath,
329                       apr_pool_t *scratch_pool)
330{
331  return svn_error_trace(
332    svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
333                         NULL, NULL, depth, NULL, NULL, NULL, NULL,
334                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
335                         NULL, NULL, NULL, NULL, NULL, NULL,
336                         wc_ctx->db, local_abspath, scratch_pool,
337                         scratch_pool));
338}
339
340svn_error_t *
341svn_wc__node_get_changed_info(svn_revnum_t *changed_rev,
342                              apr_time_t *changed_date,
343                              const char **changed_author,
344                              svn_wc_context_t *wc_ctx,
345                              const char *local_abspath,
346                              apr_pool_t *result_pool,
347                              apr_pool_t *scratch_pool)
348{
349  return svn_error_trace(
350    svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, changed_rev,
351                         changed_date, changed_author, NULL, NULL, NULL,
352                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
353                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
354                         wc_ctx->db, local_abspath, result_pool,
355                         scratch_pool));
356}
357
358svn_error_t *
359svn_wc__node_get_url(const char **url,
360                     svn_wc_context_t *wc_ctx,
361                     const char *local_abspath,
362                     apr_pool_t *result_pool,
363                     apr_pool_t *scratch_pool)
364{
365  return svn_error_trace(svn_wc__db_read_url(url, wc_ctx->db, local_abspath,
366                                             result_pool, scratch_pool));
367}
368
369/* A recursive node-walker, helper for svn_wc__internal_walk_children().
370 *
371 * Call WALK_CALLBACK with WALK_BATON on all children (recursively) of
372 * DIR_ABSPATH in DB, but not on DIR_ABSPATH itself. DIR_ABSPATH must be a
373 * versioned directory. If SHOW_HIDDEN is true, visit hidden nodes, else
374 * ignore them. Restrict the depth of the walk to DEPTH.
375 *
376 * ### Is it possible for a subdirectory to be hidden and known to be a
377 *     directory?  If so, and if show_hidden is true, this will try to
378 *     recurse into it.  */
379static svn_error_t *
380walker_helper(svn_wc__db_t *db,
381              const char *dir_abspath,
382              svn_boolean_t show_hidden,
383              const apr_hash_t *changelist_filter,
384              svn_wc__node_found_func_t walk_callback,
385              void *walk_baton,
386              svn_depth_t depth,
387              svn_cancel_func_t cancel_func,
388              void *cancel_baton,
389              apr_pool_t *scratch_pool)
390{
391  apr_hash_t *rel_children_info;
392  apr_hash_index_t *hi;
393  apr_pool_t *iterpool;
394
395  if (depth == svn_depth_empty)
396    return SVN_NO_ERROR;
397
398  SVN_ERR(svn_wc__db_read_children_walker_info(&rel_children_info, db,
399                                               dir_abspath, scratch_pool,
400                                               scratch_pool));
401
402
403  iterpool = svn_pool_create(scratch_pool);
404  for (hi = apr_hash_first(scratch_pool, rel_children_info);
405       hi;
406       hi = apr_hash_next(hi))
407    {
408      const char *child_name = svn__apr_hash_index_key(hi);
409      struct svn_wc__db_walker_info_t *wi = svn__apr_hash_index_val(hi);
410      svn_node_kind_t child_kind = wi->kind;
411      svn_wc__db_status_t child_status = wi->status;
412      const char *child_abspath;
413
414      svn_pool_clear(iterpool);
415
416      /* See if someone wants to cancel this operation. */
417      if (cancel_func)
418        SVN_ERR(cancel_func(cancel_baton));
419
420      child_abspath = svn_dirent_join(dir_abspath, child_name, iterpool);
421
422      if (!show_hidden)
423        switch (child_status)
424          {
425            case svn_wc__db_status_not_present:
426            case svn_wc__db_status_server_excluded:
427            case svn_wc__db_status_excluded:
428              continue;
429            default:
430              break;
431          }
432
433      /* Return the child, if appropriate. */
434      if ( (child_kind == svn_node_file
435             || depth >= svn_depth_immediates)
436           && svn_wc__internal_changelist_match(db, child_abspath,
437                                                changelist_filter,
438                                                scratch_pool) )
439        {
440          svn_node_kind_t kind;
441
442          SVN_ERR(convert_db_kind_to_node_kind(&kind, child_kind,
443                                               child_status, show_hidden));
444          /* ### We might want to pass child_status as well because at least
445           * ### one callee is asking for it.
446           * ### But is it OK to use an svn_wc__db type in this API?
447           * ###    Not yet, we need to get the node walker
448           * ###    libsvn_wc-internal first. -hkw */
449          SVN_ERR(walk_callback(child_abspath, kind, walk_baton, iterpool));
450        }
451
452      /* Recurse into this directory, if appropriate. */
453      if (child_kind == svn_node_dir
454            && depth >= svn_depth_immediates)
455        {
456          svn_depth_t depth_below_here = depth;
457
458          if (depth == svn_depth_immediates)
459            depth_below_here = svn_depth_empty;
460
461          SVN_ERR(walker_helper(db, child_abspath, show_hidden,
462                                changelist_filter,
463                                walk_callback, walk_baton,
464                                depth_below_here,
465                                cancel_func, cancel_baton,
466                                iterpool));
467        }
468    }
469
470  svn_pool_destroy(iterpool);
471
472  return SVN_NO_ERROR;
473}
474
475
476svn_error_t *
477svn_wc__internal_walk_children(svn_wc__db_t *db,
478                               const char *local_abspath,
479                               svn_boolean_t show_hidden,
480                               const apr_array_header_t *changelist_filter,
481                               svn_wc__node_found_func_t walk_callback,
482                               void *walk_baton,
483                               svn_depth_t walk_depth,
484                               svn_cancel_func_t cancel_func,
485                               void *cancel_baton,
486                               apr_pool_t *scratch_pool)
487{
488  svn_node_kind_t db_kind;
489  svn_node_kind_t kind;
490  svn_wc__db_status_t status;
491  apr_hash_t *changelist_hash = NULL;
492
493  SVN_ERR_ASSERT(walk_depth >= svn_depth_empty
494                 && walk_depth <= svn_depth_infinity);
495
496  if (changelist_filter && changelist_filter->nelts)
497    SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
498                                       scratch_pool));
499
500  /* Check if the node exists before the first callback */
501  SVN_ERR(svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL,
502                               NULL, NULL, NULL, NULL, NULL, NULL,
503                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
504                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
505                               db, local_abspath, scratch_pool, scratch_pool));
506
507  SVN_ERR(convert_db_kind_to_node_kind(&kind, db_kind, status, show_hidden));
508
509  if (svn_wc__internal_changelist_match(db, local_abspath,
510                                        changelist_hash, scratch_pool))
511    SVN_ERR(walk_callback(local_abspath, kind, walk_baton, scratch_pool));
512
513  if (db_kind == svn_node_file
514      || status == svn_wc__db_status_not_present
515      || status == svn_wc__db_status_excluded
516      || status == svn_wc__db_status_server_excluded)
517    return SVN_NO_ERROR;
518
519  if (db_kind == svn_node_dir)
520    {
521      return svn_error_trace(
522        walker_helper(db, local_abspath, show_hidden, changelist_hash,
523                      walk_callback, walk_baton,
524                      walk_depth, cancel_func, cancel_baton, scratch_pool));
525    }
526
527  return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
528                           _("'%s' has an unrecognized node kind"),
529                           svn_dirent_local_style(local_abspath,
530                                                  scratch_pool));
531}
532
533svn_error_t *
534svn_wc__node_is_status_deleted(svn_boolean_t *is_deleted,
535                               svn_wc_context_t *wc_ctx,
536                               const char *local_abspath,
537                               apr_pool_t *scratch_pool)
538{
539  svn_wc__db_status_t status;
540
541  SVN_ERR(svn_wc__db_read_info(&status,
542                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
543                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
544                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
545                               NULL, NULL, NULL, NULL, NULL,
546                               wc_ctx->db, local_abspath,
547                               scratch_pool, scratch_pool));
548
549  *is_deleted = (status == svn_wc__db_status_deleted);
550
551  return SVN_NO_ERROR;
552}
553
554svn_error_t *
555svn_wc__node_get_deleted_ancestor(const char **deleted_ancestor_abspath,
556                                  svn_wc_context_t *wc_ctx,
557                                  const char *local_abspath,
558                                  apr_pool_t *result_pool,
559                                  apr_pool_t *scratch_pool)
560{
561  svn_wc__db_status_t status;
562
563  *deleted_ancestor_abspath = NULL;
564
565  SVN_ERR(svn_wc__db_read_info(&status,
566                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
567                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
568                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
569                               NULL, NULL, NULL, NULL, NULL,
570                               wc_ctx->db, local_abspath,
571                               scratch_pool, scratch_pool));
572
573  if (status == svn_wc__db_status_deleted)
574    SVN_ERR(svn_wc__db_scan_deletion(deleted_ancestor_abspath, NULL, NULL,
575                                     NULL, wc_ctx->db, local_abspath,
576                                     result_pool, scratch_pool));
577
578  return SVN_NO_ERROR;
579}
580
581svn_error_t *
582svn_wc__node_is_not_present(svn_boolean_t *is_not_present,
583                            svn_boolean_t *is_excluded,
584                            svn_boolean_t *is_server_excluded,
585                            svn_wc_context_t *wc_ctx,
586                            const char *local_abspath,
587                            svn_boolean_t base_only,
588                            apr_pool_t *scratch_pool)
589{
590  svn_wc__db_status_t status;
591
592  if (base_only)
593    {
594      SVN_ERR(svn_wc__db_base_get_info(&status,
595                                       NULL, NULL, NULL, NULL, NULL, NULL,
596                                       NULL, NULL, NULL, NULL, NULL, NULL,
597                                       NULL, NULL, NULL,
598                                       wc_ctx->db, local_abspath,
599                                       scratch_pool, scratch_pool));
600    }
601  else
602    {
603      SVN_ERR(svn_wc__db_read_info(&status,
604                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
605                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
606                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
607                                   NULL, NULL, NULL, NULL, NULL,
608                                   wc_ctx->db, local_abspath,
609                                   scratch_pool, scratch_pool));
610    }
611
612  if (is_not_present)
613    *is_not_present = (status == svn_wc__db_status_not_present);
614
615  if (is_excluded)
616    *is_excluded = (status == svn_wc__db_status_excluded);
617
618  if (is_server_excluded)
619    *is_server_excluded = (status == svn_wc__db_status_server_excluded);
620
621  return SVN_NO_ERROR;
622}
623
624svn_error_t *
625svn_wc__node_is_added(svn_boolean_t *is_added,
626                      svn_wc_context_t *wc_ctx,
627                      const char *local_abspath,
628                      apr_pool_t *scratch_pool)
629{
630  svn_wc__db_status_t status;
631
632  SVN_ERR(svn_wc__db_read_info(&status,
633                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
634                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
635                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
636                               NULL, NULL, NULL, NULL, NULL,
637                               wc_ctx->db, local_abspath,
638                               scratch_pool, scratch_pool));
639  *is_added = (status == svn_wc__db_status_added);
640
641  return SVN_NO_ERROR;
642}
643
644svn_error_t *
645svn_wc__node_has_working(svn_boolean_t *has_working,
646                         svn_wc_context_t *wc_ctx,
647                         const char *local_abspath,
648                         apr_pool_t *scratch_pool)
649{
650  svn_wc__db_status_t status;
651
652  SVN_ERR(svn_wc__db_read_info(&status,
653                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
654                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
655                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
656                               NULL, NULL, NULL, NULL, has_working,
657                               wc_ctx->db, local_abspath,
658                               scratch_pool, scratch_pool));
659
660  return SVN_NO_ERROR;
661}
662
663
664svn_error_t *
665svn_wc__node_get_base(svn_node_kind_t *kind,
666                      svn_revnum_t *revision,
667                      const char **repos_relpath,
668                      const char **repos_root_url,
669                      const char **repos_uuid,
670                      const char **lock_token,
671                      svn_wc_context_t *wc_ctx,
672                      const char *local_abspath,
673                      svn_boolean_t ignore_enoent,
674                      svn_boolean_t show_hidden,
675                      apr_pool_t *result_pool,
676                      apr_pool_t *scratch_pool)
677{
678  svn_error_t *err;
679  svn_wc__db_status_t status;
680  svn_wc__db_lock_t *lock;
681  svn_node_kind_t db_kind;
682
683  err = svn_wc__db_base_get_info(&status, &db_kind, revision, repos_relpath,
684                                 repos_root_url, repos_uuid, NULL,
685                                 NULL, NULL, NULL, NULL, NULL,
686                                 lock_token ? &lock : NULL,
687                                 NULL, NULL, NULL,
688                                 wc_ctx->db, local_abspath,
689                                 result_pool, scratch_pool);
690
691  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
692    return svn_error_trace(err);
693  else if (err
694           || (!err && !show_hidden
695               && (status != svn_wc__db_status_normal
696                   && status != svn_wc__db_status_incomplete)))
697    {
698      if (!ignore_enoent)
699        {
700          if (err)
701            return svn_error_trace(err);
702          else
703            return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
704                                     _("The node '%s' was not found."),
705                                     svn_dirent_local_style(local_abspath,
706                                                            scratch_pool));
707        }
708      svn_error_clear(err);
709
710      if (kind)
711        *kind = svn_node_unknown;
712      if (revision)
713        *revision = SVN_INVALID_REVNUM;
714      if (repos_relpath)
715        *repos_relpath = NULL;
716      if (repos_root_url)
717        *repos_root_url = NULL;
718      if (repos_uuid)
719        *repos_uuid = NULL;
720      if (lock_token)
721        *lock_token = NULL;
722      return SVN_NO_ERROR;
723    }
724
725  if (kind)
726    *kind = db_kind;
727  if (lock_token)
728    *lock_token = lock ? lock->token : NULL;
729
730  SVN_ERR_ASSERT(!revision || SVN_IS_VALID_REVNUM(*revision));
731  SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
732  SVN_ERR_ASSERT(!repos_root_url || *repos_root_url);
733  SVN_ERR_ASSERT(!repos_uuid || *repos_uuid);
734  return SVN_NO_ERROR;
735}
736
737svn_error_t *
738svn_wc__node_get_pre_ng_status_data(svn_revnum_t *revision,
739                                   svn_revnum_t *changed_rev,
740                                   apr_time_t *changed_date,
741                                   const char **changed_author,
742                                   svn_wc_context_t *wc_ctx,
743                                   const char *local_abspath,
744                                   apr_pool_t *result_pool,
745                                   apr_pool_t *scratch_pool)
746{
747  svn_wc__db_status_t status;
748  svn_boolean_t have_base, have_more_work, have_work;
749
750  SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL, NULL, NULL,
751                               changed_rev, changed_date, changed_author,
752                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
753                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
754                               &have_base, &have_more_work, &have_work,
755                               wc_ctx->db, local_abspath,
756                               result_pool, scratch_pool));
757
758  if (!have_work
759      || ((!changed_rev || SVN_IS_VALID_REVNUM(*changed_rev))
760          && (!revision || SVN_IS_VALID_REVNUM(*revision)))
761      || ((status != svn_wc__db_status_added)
762          && (status != svn_wc__db_status_deleted)))
763    {
764      return SVN_NO_ERROR; /* We got everything we need */
765    }
766
767  if (have_base && !have_more_work)
768    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL,
769                                     changed_rev, changed_date, changed_author,
770                                     NULL, NULL, NULL,
771                                     NULL, NULL, NULL, NULL,
772                                     wc_ctx->db, local_abspath,
773                                     result_pool, scratch_pool));
774  else if (status == svn_wc__db_status_deleted)
775    /* Check the information below a WORKING delete */
776    SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, changed_rev,
777                                          changed_date, changed_author, NULL,
778                                          NULL, NULL, NULL, NULL,
779                                          wc_ctx->db, local_abspath,
780                                          result_pool, scratch_pool));
781
782  return SVN_NO_ERROR;
783}
784
785svn_error_t *
786svn_wc__internal_node_get_schedule(svn_wc_schedule_t *schedule,
787                                   svn_boolean_t *copied,
788                                   svn_wc__db_t *db,
789                                   const char *local_abspath,
790                                   apr_pool_t *scratch_pool)
791{
792  svn_wc__db_status_t status;
793  svn_boolean_t op_root;
794  svn_boolean_t have_base;
795  svn_boolean_t have_work;
796  svn_boolean_t have_more_work;
797  const char *copyfrom_relpath;
798
799  if (schedule)
800    *schedule = svn_wc_schedule_normal;
801  if (copied)
802    *copied = FALSE;
803
804  SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
805                               NULL, NULL, NULL, NULL, NULL, &copyfrom_relpath,
806                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
807                               &op_root, NULL, NULL,
808                               &have_base, &have_more_work, &have_work,
809                               db, local_abspath, scratch_pool, scratch_pool));
810
811  switch (status)
812    {
813      case svn_wc__db_status_not_present:
814      case svn_wc__db_status_server_excluded:
815      case svn_wc__db_status_excluded:
816        /* We used status normal in the entries world. */
817        if (schedule)
818          *schedule = svn_wc_schedule_normal;
819        break;
820      case svn_wc__db_status_normal:
821      case svn_wc__db_status_incomplete:
822        break;
823
824      case svn_wc__db_status_deleted:
825        {
826          if (schedule)
827            *schedule = svn_wc_schedule_delete;
828
829          if (!copied)
830            break;
831
832          if (have_more_work || !have_base)
833            *copied = TRUE;
834          else
835            {
836              const char *work_del_abspath;
837
838              /* Find out details of our deletion.  */
839              SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
840                                               &work_del_abspath, NULL,
841                                               db, local_abspath,
842                                               scratch_pool, scratch_pool));
843
844              if (work_del_abspath)
845                *copied = TRUE; /* Working deletion */
846            }
847          break;
848        }
849      case svn_wc__db_status_added:
850        {
851          if (!op_root)
852            {
853              if (copied)
854                *copied = TRUE;
855
856              if (schedule)
857                *schedule = svn_wc_schedule_normal;
858
859              break;
860            }
861
862          if (copied)
863            *copied = (copyfrom_relpath != NULL);
864
865          if (schedule)
866            *schedule = svn_wc_schedule_add;
867          else
868            break;
869
870          /* Check for replaced */
871          if (have_base || have_more_work)
872            {
873              svn_wc__db_status_t below_working;
874              SVN_ERR(svn_wc__db_info_below_working(&have_base, &have_work,
875                                                    &below_working,
876                                                    db, local_abspath,
877                                                    scratch_pool));
878
879              /* If the node is not present or deleted (read: not present
880                 in working), then the node is not a replacement */
881              if (below_working != svn_wc__db_status_not_present
882                  && below_working != svn_wc__db_status_deleted)
883                {
884                  *schedule = svn_wc_schedule_replace;
885                  break;
886                }
887            }
888          break;
889        }
890      default:
891        SVN_ERR_MALFUNCTION();
892    }
893
894  return SVN_NO_ERROR;
895}
896
897svn_error_t *
898svn_wc__node_get_schedule(svn_wc_schedule_t *schedule,
899                          svn_boolean_t *copied,
900                          svn_wc_context_t *wc_ctx,
901                          const char *local_abspath,
902                          apr_pool_t *scratch_pool)
903{
904  return svn_error_trace(
905           svn_wc__internal_node_get_schedule(schedule,
906                                              copied,
907                                              wc_ctx->db,
908                                              local_abspath,
909                                              scratch_pool));
910}
911
912svn_error_t *
913svn_wc__node_clear_dav_cache_recursive(svn_wc_context_t *wc_ctx,
914                                       const char *local_abspath,
915                                       apr_pool_t *scratch_pool)
916{
917  return svn_error_trace(svn_wc__db_base_clear_dav_cache_recursive(
918                              wc_ctx->db, local_abspath, scratch_pool));
919}
920
921
922svn_error_t *
923svn_wc__node_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
924                                       svn_wc_context_t *wc_ctx,
925                                       const char *local_abspath,
926                                       apr_pool_t *result_pool,
927                                       apr_pool_t *scratch_pool)
928{
929  return svn_error_trace(svn_wc__db_base_get_lock_tokens_recursive(
930                              lock_tokens, wc_ctx->db, local_abspath,
931                              result_pool, scratch_pool));
932}
933
934svn_error_t *
935svn_wc__get_excluded_subtrees(apr_hash_t **server_excluded_subtrees,
936                              svn_wc_context_t *wc_ctx,
937                              const char *local_abspath,
938                              apr_pool_t *result_pool,
939                              apr_pool_t *scratch_pool)
940{
941  return svn_error_trace(
942           svn_wc__db_get_excluded_subtrees(server_excluded_subtrees,
943                                            wc_ctx->db,
944                                            local_abspath,
945                                            result_pool,
946                                            scratch_pool));
947}
948
949svn_error_t *
950svn_wc__internal_get_origin(svn_boolean_t *is_copy,
951                            svn_revnum_t *revision,
952                            const char **repos_relpath,
953                            const char **repos_root_url,
954                            const char **repos_uuid,
955                            const char **copy_root_abspath,
956                            svn_wc__db_t *db,
957                            const char *local_abspath,
958                            svn_boolean_t scan_deleted,
959                            apr_pool_t *result_pool,
960                            apr_pool_t *scratch_pool)
961{
962  const char *original_repos_relpath;
963  const char *original_repos_root_url;
964  const char *original_repos_uuid;
965  svn_revnum_t original_revision;
966  svn_wc__db_status_t status;
967
968  const char *tmp_repos_relpath;
969
970  if (!repos_relpath)
971    repos_relpath = &tmp_repos_relpath;
972
973  SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, repos_relpath,
974                               repos_root_url, repos_uuid, NULL, NULL, NULL,
975                               NULL, NULL, NULL,
976                               &original_repos_relpath,
977                               &original_repos_root_url,
978                               &original_repos_uuid, &original_revision,
979                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
980                               NULL, NULL, is_copy,
981                               db, local_abspath, result_pool, scratch_pool));
982
983  if (*repos_relpath)
984    {
985      return SVN_NO_ERROR; /* Returned BASE information */
986    }
987
988  if (status == svn_wc__db_status_deleted && !scan_deleted)
989    {
990      if (is_copy)
991        *is_copy = FALSE; /* Deletes are stored in working; default to FALSE */
992
993      return SVN_NO_ERROR; /* No info */
994    }
995
996  if (original_repos_relpath)
997    {
998      *repos_relpath = original_repos_relpath;
999      if (revision)
1000        *revision = original_revision;
1001      if (repos_root_url)
1002        *repos_root_url = original_repos_root_url;
1003      if (repos_uuid)
1004        *repos_uuid = original_repos_uuid;
1005
1006      if (copy_root_abspath == NULL)
1007        return SVN_NO_ERROR;
1008    }
1009
1010  {
1011    svn_boolean_t scan_working = FALSE;
1012
1013    if (status == svn_wc__db_status_added)
1014      scan_working = TRUE;
1015    else if (status == svn_wc__db_status_deleted)
1016      {
1017        svn_boolean_t have_base;
1018        /* Is this a BASE or a WORKING delete? */
1019        SVN_ERR(svn_wc__db_info_below_working(&have_base, &scan_working,
1020                                              &status, db, local_abspath,
1021                                              scratch_pool));
1022      }
1023
1024    if (scan_working)
1025      {
1026        const char *op_root_abspath;
1027
1028        SVN_ERR(svn_wc__db_scan_addition(&status, &op_root_abspath, NULL,
1029                                         NULL, NULL, &original_repos_relpath,
1030                                         repos_root_url,
1031                                         repos_uuid, revision,
1032                                         db, local_abspath,
1033                                         result_pool, scratch_pool));
1034
1035        if (status == svn_wc__db_status_added)
1036          {
1037            if (is_copy)
1038              *is_copy = FALSE;
1039            return SVN_NO_ERROR; /* Local addition */
1040          }
1041
1042        /* We don't know how the following error condition can be fulfilled
1043         * but we have seen that happening in the wild.  Better to create
1044         * an error than a SEGFAULT. */
1045        if (status == svn_wc__db_status_incomplete && !original_repos_relpath)
1046          return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
1047                               _("Incomplete copy information on path '%s'."),
1048                                   svn_dirent_local_style(local_abspath,
1049                                                          scratch_pool));
1050
1051        *repos_relpath = svn_relpath_join(
1052                                original_repos_relpath,
1053                                svn_dirent_skip_ancestor(op_root_abspath,
1054                                                         local_abspath),
1055                                result_pool);
1056        if (copy_root_abspath)
1057          *copy_root_abspath = op_root_abspath;
1058      }
1059    else /* Deleted, excluded, not-present, server-excluded, ... */
1060      {
1061        if (is_copy)
1062          *is_copy = FALSE;
1063
1064        SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, repos_relpath,
1065                                         repos_root_url, repos_uuid, NULL,
1066                                         NULL, NULL, NULL, NULL, NULL, NULL,
1067                                         NULL, NULL, NULL,
1068                                         db, local_abspath,
1069                                         result_pool, scratch_pool));
1070      }
1071
1072    return SVN_NO_ERROR;
1073  }
1074}
1075
1076svn_error_t *
1077svn_wc__node_get_origin(svn_boolean_t *is_copy,
1078                        svn_revnum_t *revision,
1079                        const char **repos_relpath,
1080                        const char **repos_root_url,
1081                        const char **repos_uuid,
1082                        const char **copy_root_abspath,
1083                        svn_wc_context_t *wc_ctx,
1084                        const char *local_abspath,
1085                        svn_boolean_t scan_deleted,
1086                        apr_pool_t *result_pool,
1087                        apr_pool_t *scratch_pool)
1088{
1089  return svn_error_trace(svn_wc__internal_get_origin(is_copy, revision,
1090                           repos_relpath, repos_root_url, repos_uuid,
1091                           copy_root_abspath,
1092                           wc_ctx->db, local_abspath, scan_deleted,
1093                           result_pool, scratch_pool));
1094}
1095
1096svn_error_t *
1097svn_wc__node_get_commit_status(svn_boolean_t *added,
1098                               svn_boolean_t *deleted,
1099                               svn_boolean_t *is_replace_root,
1100                               svn_boolean_t *is_op_root,
1101                               svn_revnum_t *revision,
1102                               svn_revnum_t *original_revision,
1103                               const char **original_repos_relpath,
1104                               svn_wc_context_t *wc_ctx,
1105                               const char *local_abspath,
1106                               apr_pool_t *result_pool,
1107                               apr_pool_t *scratch_pool)
1108{
1109  svn_wc__db_status_t status;
1110  svn_boolean_t have_base;
1111  svn_boolean_t have_more_work;
1112  svn_boolean_t op_root;
1113
1114  /* ### All of this should be handled inside a single read transaction */
1115  SVN_ERR(svn_wc__db_read_info(&status, NULL, revision, NULL,
1116                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1117                               original_repos_relpath, NULL, NULL,
1118                               original_revision, NULL, NULL, NULL,
1119                               NULL, NULL,
1120                               &op_root, NULL, NULL,
1121                               &have_base, &have_more_work, NULL,
1122                               wc_ctx->db, local_abspath,
1123                               result_pool, scratch_pool));
1124
1125  if (added)
1126    *added = (status == svn_wc__db_status_added);
1127  if (deleted)
1128    *deleted = (status == svn_wc__db_status_deleted);
1129  if (is_op_root)
1130    *is_op_root = op_root;
1131
1132  if (is_replace_root)
1133    {
1134      if (status == svn_wc__db_status_added
1135          && op_root
1136          && (have_base || have_more_work))
1137        SVN_ERR(svn_wc__db_node_check_replace(is_replace_root, NULL, NULL,
1138                                              wc_ctx->db, local_abspath,
1139                                              scratch_pool));
1140      else
1141        *is_replace_root = FALSE;
1142    }
1143
1144  /* Retrieve some information from BASE which is needed for replacing
1145     and/or deleting BASE nodes. */
1146  if (have_base
1147      && !have_more_work
1148      && op_root
1149      && (revision && !SVN_IS_VALID_REVNUM(*revision)))
1150    {
1151      SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, revision, NULL, NULL, NULL,
1152                                       NULL, NULL, NULL, NULL, NULL, NULL,
1153                                       NULL, NULL, NULL, NULL,
1154                                       wc_ctx->db, local_abspath,
1155                                       scratch_pool, scratch_pool));
1156    }
1157
1158  return SVN_NO_ERROR;
1159}
1160
1161svn_error_t *
1162svn_wc__node_get_md5_from_sha1(const svn_checksum_t **md5_checksum,
1163                               svn_wc_context_t *wc_ctx,
1164                               const char *wri_abspath,
1165                               const svn_checksum_t *sha1_checksum,
1166                               apr_pool_t *result_pool,
1167                               apr_pool_t *scratch_pool)
1168{
1169  return svn_error_trace(svn_wc__db_pristine_get_md5(md5_checksum,
1170                                                     wc_ctx->db,
1171                                                     wri_abspath,
1172                                                     sha1_checksum,
1173                                                     result_pool,
1174                                                     scratch_pool));
1175}
1176
1177svn_error_t *
1178svn_wc__get_not_present_descendants(const apr_array_header_t **descendants,
1179                                    svn_wc_context_t *wc_ctx,
1180                                    const char *local_abspath,
1181                                    apr_pool_t *result_pool,
1182                                    apr_pool_t *scratch_pool)
1183{
1184  return svn_error_trace(
1185                svn_wc__db_get_not_present_descendants(descendants,
1186                                                       wc_ctx->db,
1187                                                       local_abspath,
1188                                                       result_pool,
1189                                                       scratch_pool));
1190}
1191
1192svn_error_t *
1193svn_wc__rename_wc(svn_wc_context_t *wc_ctx,
1194                  const char *from_abspath,
1195                  const char *dst_abspath,
1196                  apr_pool_t *scratch_pool)
1197{
1198  const char *wcroot_abspath;
1199  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, wc_ctx->db, from_abspath,
1200                                scratch_pool, scratch_pool));
1201
1202  if (! strcmp(from_abspath, wcroot_abspath))
1203    {
1204      SVN_ERR(svn_wc__db_drop_root(wc_ctx->db, wcroot_abspath, scratch_pool));
1205
1206      SVN_ERR(svn_io_file_rename(from_abspath, dst_abspath, scratch_pool));
1207    }
1208  else
1209    return svn_error_createf(
1210                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
1211                    _("'%s' is not the root of the working copy '%s'"),
1212                    svn_dirent_local_style(from_abspath, scratch_pool),
1213                    svn_dirent_local_style(wcroot_abspath, scratch_pool));
1214
1215  return SVN_NO_ERROR;
1216}
1217
1218svn_error_t *
1219svn_wc__check_for_obstructions(svn_wc_notify_state_t *obstruction_state,
1220                               svn_node_kind_t *kind,
1221                               svn_boolean_t *deleted,
1222                               svn_boolean_t *excluded,
1223                               svn_depth_t *parent_depth,
1224                               svn_wc_context_t *wc_ctx,
1225                               const char *local_abspath,
1226                               svn_boolean_t no_wcroot_check,
1227                               apr_pool_t *scratch_pool)
1228{
1229  svn_wc__db_status_t status;
1230  svn_node_kind_t db_kind;
1231  svn_node_kind_t disk_kind;
1232  svn_error_t *err;
1233
1234  *obstruction_state = svn_wc_notify_state_inapplicable;
1235  if (kind)
1236    *kind = svn_node_none;
1237  if (deleted)
1238    *deleted = FALSE;
1239  if (excluded)
1240    *excluded = FALSE;
1241  if (parent_depth)
1242    *parent_depth = svn_depth_unknown;
1243
1244  SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));
1245
1246  err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL, NULL,
1247                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1248                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1249                             NULL, NULL, NULL, NULL, NULL,
1250                             wc_ctx->db, local_abspath,
1251                             scratch_pool, scratch_pool);
1252
1253  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1254    {
1255      svn_error_clear(err);
1256
1257      if (disk_kind != svn_node_none)
1258        {
1259          /* Nothing in the DB, but something on disk */
1260          *obstruction_state = svn_wc_notify_state_obstructed;
1261          return SVN_NO_ERROR;
1262        }
1263
1264      err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL,
1265                                 NULL, NULL, NULL, parent_depth, NULL, NULL,
1266                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1267                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1268                                 NULL,
1269                                 wc_ctx->db, svn_dirent_dirname(local_abspath,
1270                                                                scratch_pool),
1271                                 scratch_pool, scratch_pool);
1272
1273      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1274        {
1275          svn_error_clear(err);
1276          /* No versioned parent; we can't add a node here */
1277          *obstruction_state = svn_wc_notify_state_obstructed;
1278          return SVN_NO_ERROR;
1279        }
1280      else
1281        SVN_ERR(err);
1282
1283      if (db_kind != svn_node_dir
1284          || (status != svn_wc__db_status_normal
1285              && status != svn_wc__db_status_added))
1286        {
1287          /* The parent doesn't allow nodes to be added below it */
1288          *obstruction_state = svn_wc_notify_state_obstructed;
1289        }
1290
1291      return SVN_NO_ERROR;
1292    }
1293  else
1294    SVN_ERR(err);
1295
1296  /* Check for obstructing working copies */
1297  if (!no_wcroot_check
1298      && db_kind == svn_node_dir
1299      && status == svn_wc__db_status_normal)
1300    {
1301      svn_boolean_t is_root;
1302      SVN_ERR(svn_wc__db_is_wcroot(&is_root, wc_ctx->db, local_abspath,
1303                                   scratch_pool));
1304
1305      if (is_root)
1306        {
1307          /* Callers should handle this as unversioned */
1308          *obstruction_state = svn_wc_notify_state_obstructed;
1309          return SVN_NO_ERROR;
1310        }
1311    }
1312
1313  if (kind)
1314    SVN_ERR(convert_db_kind_to_node_kind(kind, db_kind, status, FALSE));
1315
1316  switch (status)
1317    {
1318      case svn_wc__db_status_deleted:
1319        if (deleted)
1320          *deleted = TRUE;
1321        /* Fall through to svn_wc__db_status_not_present */
1322      case svn_wc__db_status_not_present:
1323        if (disk_kind != svn_node_none)
1324          *obstruction_state = svn_wc_notify_state_obstructed;
1325        break;
1326
1327      case svn_wc__db_status_excluded:
1328      case svn_wc__db_status_server_excluded:
1329        if (excluded)
1330          *excluded = TRUE;
1331        /* fall through */
1332      case svn_wc__db_status_incomplete:
1333        *obstruction_state = svn_wc_notify_state_missing;
1334        break;
1335
1336      case svn_wc__db_status_added:
1337      case svn_wc__db_status_normal:
1338        if (disk_kind == svn_node_none)
1339          *obstruction_state = svn_wc_notify_state_missing;
1340        else
1341          {
1342            svn_node_kind_t expected_kind;
1343
1344            SVN_ERR(convert_db_kind_to_node_kind(&expected_kind, db_kind,
1345                                                 status, FALSE));
1346
1347            if (disk_kind != expected_kind)
1348              *obstruction_state = svn_wc_notify_state_obstructed;
1349          }
1350        break;
1351      default:
1352        SVN_ERR_MALFUNCTION();
1353    }
1354
1355  return SVN_NO_ERROR;
1356}
1357
1358
1359svn_error_t *
1360svn_wc__node_was_moved_away(const char **moved_to_abspath,
1361                            const char **op_root_abspath,
1362                            svn_wc_context_t *wc_ctx,
1363                            const char *local_abspath,
1364                            apr_pool_t *result_pool,
1365                            apr_pool_t *scratch_pool)
1366{
1367  svn_boolean_t is_deleted;
1368
1369  if (moved_to_abspath)
1370    *moved_to_abspath = NULL;
1371  if (op_root_abspath)
1372    *op_root_abspath = NULL;
1373
1374  SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, wc_ctx, local_abspath,
1375                                         scratch_pool));
1376  if (is_deleted)
1377    SVN_ERR(svn_wc__db_scan_deletion(NULL, moved_to_abspath, NULL,
1378                                     op_root_abspath, wc_ctx->db,
1379                                     local_abspath,
1380                                     result_pool, scratch_pool));
1381
1382  return SVN_NO_ERROR;
1383}
1384
1385
1386svn_error_t *
1387svn_wc__node_was_moved_here(const char **moved_from_abspath,
1388                            const char **delete_op_root_abspath,
1389                            svn_wc_context_t *wc_ctx,
1390                            const char *local_abspath,
1391                            apr_pool_t *result_pool,
1392                            apr_pool_t *scratch_pool)
1393{
1394  svn_error_t *err;
1395
1396  if (moved_from_abspath)
1397    *moved_from_abspath = NULL;
1398  if (delete_op_root_abspath)
1399    *delete_op_root_abspath = NULL;
1400
1401  err = svn_wc__db_scan_moved(moved_from_abspath, NULL, NULL,
1402                              delete_op_root_abspath,
1403                              wc_ctx->db, local_abspath,
1404                              result_pool, scratch_pool);
1405
1406  if (err)
1407    {
1408      /* Return error for not added nodes */
1409      if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1410        return svn_error_trace(err);
1411
1412      /* Path not moved here */
1413      svn_error_clear(err);
1414      return SVN_NO_ERROR;
1415    }
1416
1417  return SVN_NO_ERROR;
1418}
1419