1251881Speter/*
2251881Speter * status.c: construct a status structure from an entry structure
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <assert.h>
27251881Speter#include <string.h>
28251881Speter
29251881Speter#include <apr_pools.h>
30251881Speter#include <apr_file_io.h>
31251881Speter#include <apr_hash.h>
32251881Speter
33251881Speter#include "svn_pools.h"
34251881Speter#include "svn_types.h"
35251881Speter#include "svn_delta.h"
36251881Speter#include "svn_string.h"
37251881Speter#include "svn_error.h"
38251881Speter#include "svn_dirent_uri.h"
39251881Speter#include "svn_path.h"
40251881Speter#include "svn_io.h"
41251881Speter#include "svn_config.h"
42251881Speter#include "svn_time.h"
43251881Speter#include "svn_hash.h"
44251881Speter#include "svn_sorts.h"
45251881Speter
46251881Speter#include "svn_private_config.h"
47251881Speter
48251881Speter#include "wc.h"
49251881Speter#include "props.h"
50251881Speter#include "entries.h"
51251881Speter#include "translate.h"
52251881Speter#include "tree_conflicts.h"
53251881Speter
54251881Speter#include "private/svn_wc_private.h"
55251881Speter#include "private/svn_fspath.h"
56251881Speter#include "private/svn_editor.h"
57251881Speter
58251881Speter
59251881Speter
60251881Speter/*** Baton used for walking the local status */
61251881Speterstruct walk_status_baton
62251881Speter{
63251881Speter  /* The DB handle for managing the working copy state. */
64251881Speter  svn_wc__db_t *db;
65251881Speter
66251881Speter  /*** External handling ***/
67251881Speter  /* Target of the status */
68251881Speter  const char *target_abspath;
69251881Speter
70251881Speter  /* Should we ignore text modifications? */
71251881Speter  svn_boolean_t ignore_text_mods;
72251881Speter
73251881Speter  /* Externals info harvested during the status run. */
74251881Speter  apr_hash_t *externals;
75251881Speter
76251881Speter  /*** Repository lock handling ***/
77251881Speter  /* The repository root URL, if set. */
78251881Speter  const char *repos_root;
79251881Speter
80251881Speter  /* Repository locks, if set. */
81251881Speter  apr_hash_t *repos_locks;
82251881Speter};
83251881Speter
84251881Speter/*** Editor batons ***/
85251881Speter
86251881Speterstruct edit_baton
87251881Speter{
88251881Speter  /* For status, the "destination" of the edit.  */
89251881Speter  const char *anchor_abspath;
90251881Speter  const char *target_abspath;
91251881Speter  const char *target_basename;
92251881Speter
93251881Speter  /* The DB handle for managing the working copy state.  */
94251881Speter  svn_wc__db_t *db;
95251881Speter  svn_wc_context_t *wc_ctx;
96251881Speter
97251881Speter  /* The overall depth of this edit (a dir baton may override this).
98251881Speter   *
99251881Speter   * If this is svn_depth_unknown, the depths found in the working
100251881Speter   * copy will govern the edit; or if the edit depth indicates a
101251881Speter   * descent deeper than the found depths are capable of, the found
102251881Speter   * depths also govern, of course (there's no point descending into
103251881Speter   * something that's not there).
104251881Speter   */
105251881Speter  svn_depth_t default_depth;
106251881Speter
107251881Speter  /* Do we want all statuses (instead of just the interesting ones) ? */
108251881Speter  svn_boolean_t get_all;
109251881Speter
110251881Speter  /* Ignore the svn:ignores. */
111251881Speter  svn_boolean_t no_ignore;
112251881Speter
113251881Speter  /* The comparison revision in the repository.  This is a reference
114251881Speter     because this editor returns this rev to the driver directly, as
115251881Speter     well as in each statushash entry. */
116251881Speter  svn_revnum_t *target_revision;
117251881Speter
118251881Speter  /* Status function/baton. */
119251881Speter  svn_wc_status_func4_t status_func;
120251881Speter  void *status_baton;
121251881Speter
122251881Speter  /* Cancellation function/baton. */
123251881Speter  svn_cancel_func_t cancel_func;
124251881Speter  void *cancel_baton;
125251881Speter
126251881Speter  /* The configured set of default ignores. */
127251881Speter  const apr_array_header_t *ignores;
128251881Speter
129251881Speter  /* Status item for the path represented by the anchor of the edit. */
130251881Speter  svn_wc_status3_t *anchor_status;
131251881Speter
132251881Speter  /* Was open_root() called for this edit drive? */
133251881Speter  svn_boolean_t root_opened;
134251881Speter
135251881Speter  /* The local status baton */
136251881Speter  struct walk_status_baton wb;
137251881Speter};
138251881Speter
139251881Speter
140251881Speterstruct dir_baton
141251881Speter{
142251881Speter  /* The path to this directory. */
143251881Speter  const char *local_abspath;
144251881Speter
145251881Speter  /* Basename of this directory. */
146251881Speter  const char *name;
147251881Speter
148251881Speter  /* The global edit baton. */
149251881Speter  struct edit_baton *edit_baton;
150251881Speter
151251881Speter  /* Baton for this directory's parent, or NULL if this is the root
152251881Speter     directory. */
153251881Speter  struct dir_baton *parent_baton;
154251881Speter
155251881Speter  /* The ambient requested depth below this point in the edit.  This
156251881Speter     can differ from the parent baton's depth (with the edit baton
157251881Speter     considered the ultimate parent baton).  For example, if the
158251881Speter     parent baton has svn_depth_immediates, then here we should have
159251881Speter     svn_depth_empty, because there would be no further recursion, not
160251881Speter     even to file children. */
161251881Speter  svn_depth_t depth;
162251881Speter
163251881Speter  /* Is this directory filtered out due to depth?  (Note that if this
164251881Speter     is TRUE, the depth field is undefined.) */
165251881Speter  svn_boolean_t excluded;
166251881Speter
167251881Speter  /* 'svn status' shouldn't print status lines for things that are
168251881Speter     added;  we're only interest in asking if objects that the user
169251881Speter     *already* has are up-to-date or not.  Thus if this flag is set,
170251881Speter     the next two will be ignored.  :-)  */
171251881Speter  svn_boolean_t added;
172251881Speter
173251881Speter  /* Gets set iff there's a change to this directory's properties, to
174251881Speter     guide us when syncing adm files later. */
175251881Speter  svn_boolean_t prop_changed;
176251881Speter
177251881Speter  /* This means (in terms of 'svn status') that some child was deleted
178251881Speter     or added to the directory */
179251881Speter  svn_boolean_t text_changed;
180251881Speter
181251881Speter  /* Working copy status structures for children of this directory.
182251881Speter     This hash maps const char * abspaths  to svn_wc_status3_t *
183251881Speter     status items. */
184251881Speter  apr_hash_t *statii;
185251881Speter
186251881Speter  /* The pool in which this baton itself is allocated. */
187251881Speter  apr_pool_t *pool;
188251881Speter
189251881Speter  /* The repository root relative path to this item in the repository. */
190251881Speter  const char *repos_relpath;
191251881Speter
192251881Speter  /* out-of-date info corresponding to ood_* fields in svn_wc_status3_t. */
193251881Speter  svn_node_kind_t ood_kind;
194251881Speter  svn_revnum_t ood_changed_rev;
195251881Speter  apr_time_t ood_changed_date;
196251881Speter  const char *ood_changed_author;
197251881Speter};
198251881Speter
199251881Speter
200251881Speterstruct file_baton
201251881Speter{
202251881Speter/* Absolute local path to this file */
203251881Speter  const char *local_abspath;
204251881Speter
205251881Speter  /* The global edit baton. */
206251881Speter  struct edit_baton *edit_baton;
207251881Speter
208251881Speter  /* Baton for this file's parent directory. */
209251881Speter  struct dir_baton *dir_baton;
210251881Speter
211251881Speter  /* Pool specific to this file_baton. */
212251881Speter  apr_pool_t *pool;
213251881Speter
214251881Speter  /* Basename of this file */
215251881Speter  const char *name;
216251881Speter
217251881Speter  /* 'svn status' shouldn't print status lines for things that are
218251881Speter     added;  we're only interest in asking if objects that the user
219251881Speter     *already* has are up-to-date or not.  Thus if this flag is set,
220251881Speter     the next two will be ignored.  :-)  */
221251881Speter  svn_boolean_t added;
222251881Speter
223251881Speter  /* This gets set if the file underwent a text change, which guides
224251881Speter     the code that syncs up the adm dir and working copy. */
225251881Speter  svn_boolean_t text_changed;
226251881Speter
227251881Speter  /* This gets set if the file underwent a prop change, which guides
228251881Speter     the code that syncs up the adm dir and working copy. */
229251881Speter  svn_boolean_t prop_changed;
230251881Speter
231251881Speter  /* The repository root relative path to this item in the repository. */
232251881Speter  const char *repos_relpath;
233251881Speter
234251881Speter  /* out-of-date info corresponding to ood_* fields in svn_wc_status3_t. */
235251881Speter  svn_node_kind_t ood_kind;
236251881Speter  svn_revnum_t ood_changed_rev;
237251881Speter  apr_time_t ood_changed_date;
238251881Speter
239251881Speter  const char *ood_changed_author;
240251881Speter};
241251881Speter
242251881Speter
243251881Speter/** Code **/
244251881Speter
245251881Speter
246251881Speter
247251881Speter/* Return *REPOS_RELPATH and *REPOS_ROOT_URL for LOCAL_ABSPATH using
248251881Speter   information in INFO if available, falling back on
249251881Speter   PARENT_REPOS_RELPATH and PARENT_REPOS_ROOT_URL if available, and
250251881Speter   finally falling back on querying DB. */
251251881Speterstatic svn_error_t *
252251881Speterget_repos_root_url_relpath(const char **repos_relpath,
253251881Speter                           const char **repos_root_url,
254251881Speter                           const char **repos_uuid,
255251881Speter                           const struct svn_wc__db_info_t *info,
256251881Speter                           const char *parent_repos_relpath,
257251881Speter                           const char *parent_repos_root_url,
258251881Speter                           const char *parent_repos_uuid,
259251881Speter                           svn_wc__db_t *db,
260251881Speter                           const char *local_abspath,
261251881Speter                           apr_pool_t *result_pool,
262251881Speter                           apr_pool_t *scratch_pool)
263251881Speter{
264251881Speter  if (info->repos_relpath && info->repos_root_url)
265251881Speter    {
266251881Speter      *repos_relpath = apr_pstrdup(result_pool, info->repos_relpath);
267251881Speter      *repos_root_url = apr_pstrdup(result_pool, info->repos_root_url);
268251881Speter      *repos_uuid = apr_pstrdup(result_pool, info->repos_uuid);
269251881Speter    }
270251881Speter  else if (parent_repos_relpath && parent_repos_root_url)
271251881Speter    {
272251881Speter      *repos_relpath = svn_relpath_join(parent_repos_relpath,
273251881Speter                                        svn_dirent_basename(local_abspath,
274251881Speter                                                            NULL),
275251881Speter                                        result_pool);
276251881Speter      *repos_root_url = apr_pstrdup(result_pool, parent_repos_root_url);
277251881Speter      *repos_uuid = apr_pstrdup(result_pool, parent_repos_uuid);
278251881Speter    }
279251881Speter  else if (info->status == svn_wc__db_status_added)
280251881Speter    {
281251881Speter      SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
282251881Speter                                       repos_relpath, repos_root_url,
283251881Speter                                       repos_uuid, NULL, NULL, NULL, NULL,
284251881Speter                                       db, local_abspath,
285251881Speter                                       result_pool, scratch_pool));
286251881Speter    }
287269847Speter  else if (info->status == svn_wc__db_status_deleted
288269847Speter           && !info->have_more_work
289269847Speter           && info->have_base)
290251881Speter    {
291251881Speter      SVN_ERR(svn_wc__db_scan_base_repos(repos_relpath, repos_root_url,
292251881Speter                                         repos_uuid,
293251881Speter                                         db, local_abspath,
294251881Speter                                         result_pool, scratch_pool));
295251881Speter    }
296269847Speter  else if (info->status == svn_wc__db_status_deleted)
297269847Speter    {
298269847Speter      const char *work_del_abspath;
299269847Speter      const char *add_abspath;
300269847Speter
301269847Speter      /* Handles working DELETE and the special case where there is just
302269847Speter         svn_wc__db_status_not_present in WORKING */
303269847Speter
304269847Speter      SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, &work_del_abspath, NULL,
305269847Speter                                       db, local_abspath,
306269847Speter                                       scratch_pool, scratch_pool));
307269847Speter
308269847Speter      /* The parent of what has been deleted must be added */
309269847Speter      add_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool);
310269847Speter
311269847Speter      SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, repos_relpath,
312269847Speter                                       repos_root_url, repos_uuid, NULL,
313269847Speter                                       NULL, NULL, NULL,
314269847Speter                                       db, add_abspath,
315269847Speter                                       result_pool, scratch_pool));
316269847Speter
317269847Speter      *repos_relpath = svn_relpath_join(*repos_relpath,
318269847Speter                                        svn_dirent_skip_ancestor(
319269847Speter                                              add_abspath,
320269847Speter                                              local_abspath),
321269847Speter                                        result_pool);
322269847Speter    }
323251881Speter  else
324251881Speter    {
325251881Speter      *repos_relpath = NULL;
326251881Speter      *repos_root_url = NULL;
327251881Speter      *repos_uuid = NULL;
328251881Speter    }
329251881Speter  return SVN_NO_ERROR;
330251881Speter}
331251881Speter
332251881Speterstatic svn_error_t *
333251881Speterinternal_status(svn_wc_status3_t **status,
334251881Speter                svn_wc__db_t *db,
335251881Speter                const char *local_abspath,
336251881Speter                apr_pool_t *result_pool,
337251881Speter                apr_pool_t *scratch_pool);
338251881Speter
339251881Speter/* Fill in *STATUS for LOCAL_ABSPATH, using DB. Allocate *STATUS in
340251881Speter   RESULT_POOL and use SCRATCH_POOL for temporary allocations.
341251881Speter
342251881Speter   PARENT_REPOS_ROOT_URL and PARENT_REPOS_RELPATH are the repository root
343251881Speter   and repository relative path of the parent of LOCAL_ABSPATH or NULL if
344251881Speter   LOCAL_ABSPATH doesn't have a versioned parent directory.
345251881Speter
346251881Speter   DIRENT is the local representation of LOCAL_ABSPATH in the working copy or
347251881Speter   NULL if the node does not exist on disk.
348251881Speter
349251881Speter   If GET_ALL is FALSE, and LOCAL_ABSPATH is not locally modified, then
350251881Speter   *STATUS will be set to NULL.  If GET_ALL is non-zero, then *STATUS will be
351251881Speter   allocated and returned no matter what.  If IGNORE_TEXT_MODS is TRUE then
352251881Speter   don't check for text mods, assume there are none and set and *STATUS
353251881Speter   returned to reflect that assumption.
354251881Speter
355251881Speter   The status struct's repos_lock field will be set to REPOS_LOCK.
356251881Speter*/
357251881Speterstatic svn_error_t *
358251881Speterassemble_status(svn_wc_status3_t **status,
359251881Speter                svn_wc__db_t *db,
360251881Speter                const char *local_abspath,
361251881Speter                const char *parent_repos_root_url,
362251881Speter                const char *parent_repos_relpath,
363251881Speter                const char *parent_repos_uuid,
364251881Speter                const struct svn_wc__db_info_t *info,
365251881Speter                const svn_io_dirent2_t *dirent,
366251881Speter                svn_boolean_t get_all,
367251881Speter                svn_boolean_t ignore_text_mods,
368251881Speter                const svn_lock_t *repos_lock,
369251881Speter                apr_pool_t *result_pool,
370251881Speter                apr_pool_t *scratch_pool)
371251881Speter{
372251881Speter  svn_wc_status3_t *stat;
373251881Speter  svn_boolean_t switched_p = FALSE;
374251881Speter  svn_boolean_t copied = FALSE;
375251881Speter  svn_boolean_t conflicted;
376251881Speter  const char *moved_from_abspath = NULL;
377251881Speter  svn_filesize_t filesize = (dirent && (dirent->kind == svn_node_file))
378251881Speter                                ? dirent->filesize
379251881Speter                                : SVN_INVALID_FILESIZE;
380251881Speter
381251881Speter  /* Defaults for two main variables. */
382251881Speter  enum svn_wc_status_kind node_status = svn_wc_status_normal;
383251881Speter  enum svn_wc_status_kind text_status = svn_wc_status_normal;
384251881Speter  enum svn_wc_status_kind prop_status = svn_wc_status_none;
385251881Speter
386251881Speter
387251881Speter  if (!info)
388269847Speter    SVN_ERR(svn_wc__db_read_single_info(&info, db, local_abspath,
389269847Speter                                        result_pool, scratch_pool));
390251881Speter
391251881Speter  if (!info->repos_relpath || !parent_repos_relpath)
392251881Speter    switched_p = FALSE;
393251881Speter  else
394251881Speter    {
395251881Speter      /* A node is switched if it doesn't have the implied repos_relpath */
396251881Speter      const char *name = svn_relpath_skip_ancestor(parent_repos_relpath,
397251881Speter                                                   info->repos_relpath);
398251881Speter      switched_p = !name || (strcmp(name,
399251881Speter                                    svn_dirent_basename(local_abspath, NULL))
400251881Speter                             != 0);
401251881Speter    }
402251881Speter
403251881Speter  if (info->status == svn_wc__db_status_incomplete || info->incomplete)
404251881Speter    {
405251881Speter      /* Highest precedence.  */
406251881Speter      node_status = svn_wc_status_incomplete;
407251881Speter    }
408251881Speter  else if (info->status == svn_wc__db_status_deleted)
409251881Speter    {
410251881Speter      node_status = svn_wc_status_deleted;
411251881Speter
412251881Speter      if (!info->have_base || info->have_more_work || info->copied)
413251881Speter        copied = TRUE;
414251881Speter      else if (!info->have_more_work && info->have_base)
415251881Speter        copied = FALSE;
416251881Speter      else
417251881Speter        {
418251881Speter          const char *work_del_abspath;
419251881Speter
420251881Speter          /* Find out details of our deletion.  */
421251881Speter          SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
422251881Speter                                           &work_del_abspath, NULL,
423251881Speter                                           db, local_abspath,
424251881Speter                                           scratch_pool, scratch_pool));
425251881Speter          if (work_del_abspath)
426251881Speter            copied = TRUE; /* Working deletion */
427251881Speter        }
428251881Speter    }
429251881Speter  else
430251881Speter    {
431251881Speter      /* Examine whether our target is missing or obstructed. To detect
432251881Speter       * obstructions, we have to look at the on-disk status in DIRENT. */
433251881Speter      svn_node_kind_t expected_kind = (info->kind == svn_node_dir)
434251881Speter                                        ? svn_node_dir
435251881Speter                                        : svn_node_file;
436251881Speter
437251881Speter      if (!dirent || dirent->kind != expected_kind)
438251881Speter        {
439251881Speter          /* A present or added node should be on disk, so it is
440251881Speter             reported missing or obstructed.  */
441251881Speter          if (!dirent || dirent->kind == svn_node_none)
442251881Speter            node_status = svn_wc_status_missing;
443251881Speter          else
444251881Speter            node_status = svn_wc_status_obstructed;
445251881Speter        }
446251881Speter    }
447251881Speter
448251881Speter  /* Does the node have props? */
449251881Speter  if (info->status != svn_wc__db_status_deleted)
450251881Speter    {
451251881Speter      if (info->props_mod)
452251881Speter        prop_status = svn_wc_status_modified;
453251881Speter      else if (info->had_props)
454251881Speter        prop_status = svn_wc_status_normal;
455251881Speter    }
456251881Speter
457251881Speter  /* If NODE_STATUS is still normal, after the above checks, then
458251881Speter     we should proceed to refine the status.
459251881Speter
460251881Speter     If it was changed, then the subdir is incomplete or missing/obstructed.
461251881Speter   */
462251881Speter  if (info->kind != svn_node_dir
463251881Speter      && node_status == svn_wc_status_normal)
464251881Speter    {
465251881Speter      svn_boolean_t text_modified_p = FALSE;
466251881Speter
467251881Speter      /* Implement predecence rules: */
468251881Speter
469251881Speter      /* 1. Set the two main variables to "discovered" values first (M, C).
470251881Speter            Together, these two stati are of lowest precedence, and C has
471251881Speter            precedence over M. */
472251881Speter
473251881Speter      /* If the entry is a file, check for textual modifications */
474251881Speter      if ((info->kind == svn_node_file
475251881Speter          || info->kind == svn_node_symlink)
476251881Speter#ifdef HAVE_SYMLINK
477251881Speter             && (info->special == (dirent && dirent->special))
478251881Speter#endif /* HAVE_SYMLINK */
479251881Speter          )
480251881Speter        {
481251881Speter          /* If the on-disk dirent exactly matches the expected state
482251881Speter             skip all operations in svn_wc__internal_text_modified_p()
483251881Speter             to avoid an extra filestat for every file, which can be
484251881Speter             expensive on network drives as a filestat usually can't
485251881Speter             be cached there */
486251881Speter          if (!info->has_checksum)
487251881Speter            text_modified_p = TRUE; /* Local addition -> Modified */
488251881Speter          else if (ignore_text_mods
489251881Speter                  ||(dirent
490251881Speter                     && info->recorded_size != SVN_INVALID_FILESIZE
491251881Speter                     && info->recorded_time != 0
492251881Speter                     && info->recorded_size == dirent->filesize
493251881Speter                     && info->recorded_time == dirent->mtime))
494251881Speter            text_modified_p = FALSE;
495251881Speter          else
496251881Speter            {
497251881Speter              svn_error_t *err;
498251881Speter              err = svn_wc__internal_file_modified_p(&text_modified_p,
499251881Speter                                                     db, local_abspath,
500251881Speter                                                     FALSE, scratch_pool);
501251881Speter
502251881Speter              if (err)
503251881Speter                {
504251881Speter                  if (err->apr_err != SVN_ERR_WC_PATH_ACCESS_DENIED)
505251881Speter                    return svn_error_trace(err);
506251881Speter
507251881Speter                  /* An access denied is very common on Windows when another
508251881Speter                     application has the file open.  Previously we ignored
509251881Speter                     this error in svn_wc__text_modified_internal_p, where it
510251881Speter                     should have really errored. */
511251881Speter                  svn_error_clear(err);
512251881Speter                  text_modified_p = TRUE;
513251881Speter                }
514251881Speter            }
515251881Speter        }
516251881Speter#ifdef HAVE_SYMLINK
517251881Speter      else if (info->special != (dirent && dirent->special))
518251881Speter        node_status = svn_wc_status_obstructed;
519251881Speter#endif /* HAVE_SYMLINK */
520251881Speter
521251881Speter      if (text_modified_p)
522251881Speter        text_status = svn_wc_status_modified;
523251881Speter    }
524251881Speter
525251881Speter  conflicted = info->conflicted;
526251881Speter  if (conflicted)
527251881Speter    {
528251881Speter      svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
529251881Speter
530251881Speter      /* ### Check if the conflict was resolved by removing the marker files.
531251881Speter         ### This should really be moved to the users of this API */
532251881Speter      SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted, &prop_conflicted,
533251881Speter                                            &tree_conflicted,
534251881Speter                                            db, local_abspath, scratch_pool));
535251881Speter
536251881Speter      if (!text_conflicted && !prop_conflicted && !tree_conflicted)
537251881Speter        conflicted = FALSE;
538251881Speter    }
539251881Speter
540251881Speter  if (node_status == svn_wc_status_normal)
541251881Speter    {
542251881Speter      /* 2. Possibly overwrite the text_status variable with "scheduled"
543251881Speter            states from the entry (A, D, R).  As a group, these states are
544251881Speter            of medium precedence.  They also override any C or M that may
545251881Speter            be in the prop_status field at this point, although they do not
546251881Speter            override a C text status.*/
547251881Speter      if (info->status == svn_wc__db_status_added)
548251881Speter        {
549251881Speter          copied = info->copied;
550251881Speter          if (!info->op_root)
551251881Speter            { /* Keep status normal */ }
552251881Speter          else if (!info->have_base && !info->have_more_work)
553251881Speter            {
554251881Speter              /* Simple addition or copy, no replacement */
555251881Speter              node_status = svn_wc_status_added;
556251881Speter            }
557251881Speter          else
558251881Speter            {
559251881Speter              svn_wc__db_status_t below_working;
560251881Speter              svn_boolean_t have_base, have_work;
561251881Speter
562251881Speter              SVN_ERR(svn_wc__db_info_below_working(&have_base, &have_work,
563251881Speter                                                    &below_working,
564251881Speter                                                    db, local_abspath,
565251881Speter                                                    scratch_pool));
566251881Speter
567251881Speter              /* If the node is not present or deleted (read: not present
568251881Speter                 in working), then the node is not a replacement */
569251881Speter              if (below_working != svn_wc__db_status_not_present
570251881Speter                  && below_working != svn_wc__db_status_deleted)
571251881Speter                {
572251881Speter                  node_status = svn_wc_status_replaced;
573251881Speter                }
574251881Speter              else
575251881Speter                node_status = svn_wc_status_added;
576251881Speter            }
577251881Speter
578251881Speter          /* Get moved-from info (only for potential op-roots of a move). */
579251881Speter          if (info->moved_here && info->op_root)
580251881Speter            {
581251881Speter              svn_error_t *err;
582251881Speter              err = svn_wc__db_scan_moved(&moved_from_abspath, NULL, NULL, NULL,
583251881Speter                                          db, local_abspath,
584251881Speter                                          result_pool, scratch_pool);
585251881Speter
586251881Speter              if (err)
587251881Speter                {
588251881Speter                  if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
589251881Speter                    return svn_error_trace(err);
590251881Speter
591251881Speter                  svn_error_clear(err);
592251881Speter                  /* We are no longer moved... So most likely we are somehow
593251881Speter                     changing the db for things like resolving conflicts. */
594251881Speter
595251881Speter                  moved_from_abspath = NULL;
596251881Speter                }
597251881Speter            }
598251881Speter        }
599251881Speter    }
600251881Speter
601251881Speter
602251881Speter  if (node_status == svn_wc_status_normal)
603251881Speter    node_status = text_status;
604251881Speter
605251881Speter  if (node_status == svn_wc_status_normal
606251881Speter      && prop_status != svn_wc_status_none)
607251881Speter    node_status = prop_status;
608251881Speter
609251881Speter  /* 5. Easy out:  unless we're fetching -every- entry, don't bother
610251881Speter     to allocate a struct for an uninteresting entry. */
611251881Speter
612251881Speter  if (! get_all)
613251881Speter    if (((node_status == svn_wc_status_none)
614251881Speter         || (node_status == svn_wc_status_normal))
615251881Speter
616251881Speter        && (! switched_p)
617251881Speter        && (! info->locked )
618251881Speter        && (! info->lock)
619251881Speter        && (! repos_lock)
620251881Speter        && (! info->changelist)
621251881Speter        && (! conflicted))
622251881Speter      {
623251881Speter        *status = NULL;
624251881Speter        return SVN_NO_ERROR;
625251881Speter      }
626251881Speter
627251881Speter  /* 6. Build and return a status structure. */
628251881Speter
629251881Speter  stat = apr_pcalloc(result_pool, sizeof(**status));
630251881Speter
631251881Speter  switch (info->kind)
632251881Speter    {
633251881Speter      case svn_node_dir:
634251881Speter        stat->kind = svn_node_dir;
635251881Speter        break;
636251881Speter      case svn_node_file:
637251881Speter      case svn_node_symlink:
638251881Speter        stat->kind = svn_node_file;
639251881Speter        break;
640251881Speter      case svn_node_unknown:
641251881Speter      default:
642251881Speter        stat->kind = svn_node_unknown;
643251881Speter    }
644251881Speter  stat->depth = info->depth;
645251881Speter  stat->filesize = filesize;
646251881Speter  stat->node_status = node_status;
647251881Speter  stat->text_status = text_status;
648251881Speter  stat->prop_status = prop_status;
649251881Speter  stat->repos_node_status = svn_wc_status_none;   /* default */
650251881Speter  stat->repos_text_status = svn_wc_status_none;   /* default */
651251881Speter  stat->repos_prop_status = svn_wc_status_none;   /* default */
652251881Speter  stat->switched = switched_p;
653251881Speter  stat->copied = copied;
654251881Speter  stat->repos_lock = repos_lock;
655251881Speter  stat->revision = info->revnum;
656251881Speter  stat->changed_rev = info->changed_rev;
657251881Speter  if (info->changed_author)
658251881Speter    stat->changed_author = apr_pstrdup(result_pool, info->changed_author);
659251881Speter  stat->changed_date = info->changed_date;
660251881Speter
661251881Speter  stat->ood_kind = svn_node_none;
662251881Speter  stat->ood_changed_rev = SVN_INVALID_REVNUM;
663251881Speter  stat->ood_changed_date = 0;
664251881Speter  stat->ood_changed_author = NULL;
665251881Speter
666251881Speter  SVN_ERR(get_repos_root_url_relpath(&stat->repos_relpath,
667251881Speter                                     &stat->repos_root_url,
668251881Speter                                     &stat->repos_uuid, info,
669251881Speter                                     parent_repos_relpath,
670251881Speter                                     parent_repos_root_url,
671251881Speter                                     parent_repos_uuid,
672251881Speter                                     db, local_abspath,
673251881Speter                                     result_pool, scratch_pool));
674251881Speter
675251881Speter  if (info->lock)
676251881Speter    {
677251881Speter      svn_lock_t *lck = svn_lock_create(result_pool);
678251881Speter      lck->path = stat->repos_relpath;
679251881Speter      lck->token = info->lock->token;
680251881Speter      lck->owner = info->lock->owner;
681251881Speter      lck->comment = info->lock->comment;
682251881Speter      lck->creation_date = info->lock->date;
683251881Speter      stat->lock = lck;
684251881Speter    }
685251881Speter  else
686251881Speter    stat->lock = NULL;
687251881Speter
688251881Speter  stat->locked = info->locked;
689251881Speter  stat->conflicted = conflicted;
690251881Speter  stat->versioned = TRUE;
691251881Speter  if (info->changelist)
692251881Speter    stat->changelist = apr_pstrdup(result_pool, info->changelist);
693251881Speter
694251881Speter  stat->moved_from_abspath = moved_from_abspath;
695251881Speter
696269847Speter  /* ### TODO: Handle multiple moved_to values properly */
697269847Speter  if (info->moved_to)
698269847Speter    stat->moved_to_abspath = apr_pstrdup(result_pool,
699269847Speter                                         info->moved_to->moved_to_abspath);
700269847Speter
701251881Speter  stat->file_external = info->file_external;
702251881Speter
703251881Speter  *status = stat;
704251881Speter
705251881Speter  return SVN_NO_ERROR;
706251881Speter}
707251881Speter
708251881Speter/* Fill in *STATUS for the unversioned path LOCAL_ABSPATH, using data
709251881Speter   available in DB. Allocate *STATUS in POOL. Use SCRATCH_POOL for
710251881Speter   temporary allocations.
711251881Speter
712251881Speter   If IS_IGNORED is non-zero and this is a non-versioned entity, set
713251881Speter   the node_status to svn_wc_status_none.  Otherwise set the
714251881Speter   node_status to svn_wc_status_unversioned.
715251881Speter */
716251881Speterstatic svn_error_t *
717251881Speterassemble_unversioned(svn_wc_status3_t **status,
718251881Speter                     svn_wc__db_t *db,
719251881Speter                     const char *local_abspath,
720251881Speter                     const svn_io_dirent2_t *dirent,
721251881Speter                     svn_boolean_t tree_conflicted,
722251881Speter                     svn_boolean_t is_ignored,
723251881Speter                     apr_pool_t *result_pool,
724251881Speter                     apr_pool_t *scratch_pool)
725251881Speter{
726251881Speter  svn_wc_status3_t *stat;
727251881Speter
728251881Speter  /* return a fairly blank structure. */
729251881Speter  stat = apr_pcalloc(result_pool, sizeof(*stat));
730251881Speter
731251881Speter  /*stat->versioned = FALSE;*/
732251881Speter  stat->kind = svn_node_unknown; /* not versioned */
733251881Speter  stat->depth = svn_depth_unknown;
734251881Speter  stat->filesize = (dirent && dirent->kind == svn_node_file)
735251881Speter                        ? dirent->filesize
736251881Speter                        : SVN_INVALID_FILESIZE;
737251881Speter  stat->node_status = svn_wc_status_none;
738251881Speter  stat->text_status = svn_wc_status_none;
739251881Speter  stat->prop_status = svn_wc_status_none;
740251881Speter  stat->repos_node_status = svn_wc_status_none;
741251881Speter  stat->repos_text_status = svn_wc_status_none;
742251881Speter  stat->repos_prop_status = svn_wc_status_none;
743251881Speter
744251881Speter  /* If this path has no entry, but IS present on disk, it's
745251881Speter     unversioned.  If this file is being explicitly ignored (due
746251881Speter     to matching an ignore-pattern), the node_status is set to
747251881Speter     svn_wc_status_ignored.  Otherwise the node_status is set to
748251881Speter     svn_wc_status_unversioned. */
749251881Speter  if (dirent && dirent->kind != svn_node_none)
750251881Speter    {
751251881Speter      if (is_ignored)
752251881Speter        stat->node_status = svn_wc_status_ignored;
753251881Speter      else
754251881Speter        stat->node_status = svn_wc_status_unversioned;
755251881Speter    }
756251881Speter  else if (tree_conflicted)
757251881Speter    {
758251881Speter      /* If this path has no entry, is NOT present on disk, and IS a
759251881Speter         tree conflict victim, report it as conflicted. */
760251881Speter      stat->node_status = svn_wc_status_conflicted;
761251881Speter    }
762251881Speter
763251881Speter  stat->revision = SVN_INVALID_REVNUM;
764251881Speter  stat->changed_rev = SVN_INVALID_REVNUM;
765251881Speter  stat->ood_changed_rev = SVN_INVALID_REVNUM;
766251881Speter  stat->ood_kind = svn_node_none;
767251881Speter
768251881Speter  /* For the case of an incoming delete to a locally deleted path during
769251881Speter     an update, we get a tree conflict. */
770251881Speter  stat->conflicted = tree_conflicted;
771251881Speter  stat->changelist = NULL;
772251881Speter
773251881Speter  *status = stat;
774251881Speter  return SVN_NO_ERROR;
775251881Speter}
776251881Speter
777251881Speter
778251881Speter/* Given an ENTRY object representing PATH, build a status structure
779251881Speter   and pass it off to the STATUS_FUNC/STATUS_BATON.  All other
780251881Speter   arguments are the same as those passed to assemble_status().  */
781251881Speterstatic svn_error_t *
782251881Spetersend_status_structure(const struct walk_status_baton *wb,
783251881Speter                      const char *local_abspath,
784251881Speter                      const char *parent_repos_root_url,
785251881Speter                      const char *parent_repos_relpath,
786251881Speter                      const char *parent_repos_uuid,
787251881Speter                      const struct svn_wc__db_info_t *info,
788251881Speter                      const svn_io_dirent2_t *dirent,
789251881Speter                      svn_boolean_t get_all,
790251881Speter                      svn_wc_status_func4_t status_func,
791251881Speter                      void *status_baton,
792251881Speter                      apr_pool_t *scratch_pool)
793251881Speter{
794251881Speter  svn_wc_status3_t *statstruct;
795251881Speter  const svn_lock_t *repos_lock = NULL;
796251881Speter
797251881Speter  /* Check for a repository lock. */
798251881Speter  if (wb->repos_locks)
799251881Speter    {
800251881Speter      const char *repos_relpath, *repos_root_url, *repos_uuid;
801251881Speter
802251881Speter      SVN_ERR(get_repos_root_url_relpath(&repos_relpath, &repos_root_url,
803251881Speter                                         &repos_uuid,
804251881Speter                                         info, parent_repos_relpath,
805251881Speter                                         parent_repos_root_url,
806251881Speter                                         parent_repos_uuid,
807251881Speter                                         wb->db, local_abspath,
808251881Speter                                         scratch_pool, scratch_pool));
809251881Speter      if (repos_relpath)
810251881Speter        {
811251881Speter          /* repos_lock still uses the deprecated filesystem absolute path
812251881Speter             format */
813251881Speter          repos_lock = svn_hash_gets(wb->repos_locks,
814251881Speter                                     svn_fspath__join("/", repos_relpath,
815251881Speter                                                      scratch_pool));
816251881Speter        }
817251881Speter    }
818251881Speter
819251881Speter  SVN_ERR(assemble_status(&statstruct, wb->db, local_abspath,
820251881Speter                          parent_repos_root_url, parent_repos_relpath,
821251881Speter                          parent_repos_uuid,
822251881Speter                          info, dirent, get_all, wb->ignore_text_mods,
823251881Speter                          repos_lock, scratch_pool, scratch_pool));
824251881Speter
825251881Speter  if (statstruct && status_func)
826251881Speter    return svn_error_trace((*status_func)(status_baton, local_abspath,
827251881Speter                                          statstruct, scratch_pool));
828251881Speter
829251881Speter  return SVN_NO_ERROR;
830251881Speter}
831251881Speter
832251881Speter
833251881Speter/* Store in *PATTERNS a list of ignores collected from svn:ignore properties
834251881Speter   on LOCAL_ABSPATH and svn:global-ignores on LOCAL_ABSPATH and its
835251881Speter   repository ancestors (as cached in the working copy), including the default
836251881Speter   ignores passed in as IGNORES.
837251881Speter
838251881Speter   Upon return, *PATTERNS will contain zero or more (const char *)
839251881Speter   patterns from the value of the SVN_PROP_IGNORE property set on
840251881Speter   the working directory path.
841251881Speter
842251881Speter   IGNORES is a list of patterns to include; typically this will
843251881Speter   be the default ignores as, for example, specified in a config file.
844251881Speter
845251881Speter   DB, LOCAL_ABSPATH is used to access the working copy.
846251881Speter
847251881Speter   Allocate results in RESULT_POOL, temporary stuffs in SCRATCH_POOL.
848251881Speter
849251881Speter   None of the arguments may be NULL.
850251881Speter*/
851251881Speterstatic svn_error_t *
852251881Spetercollect_ignore_patterns(apr_array_header_t **patterns,
853251881Speter                        svn_wc__db_t *db,
854251881Speter                        const char *local_abspath,
855251881Speter                        const apr_array_header_t *ignores,
856251881Speter                        apr_pool_t *result_pool,
857251881Speter                        apr_pool_t *scratch_pool)
858251881Speter{
859251881Speter  int i;
860251881Speter  apr_hash_t *props;
861251881Speter  apr_array_header_t *inherited_props;
862251881Speter  svn_error_t *err;
863251881Speter
864251881Speter  /* ### assert we are passed a directory? */
865251881Speter
866251881Speter  *patterns = apr_array_make(result_pool, 1, sizeof(const char *));
867251881Speter
868251881Speter  /* Copy default ignores into the local PATTERNS array. */
869251881Speter  for (i = 0; i < ignores->nelts; i++)
870251881Speter    {
871251881Speter      const char *ignore = APR_ARRAY_IDX(ignores, i, const char *);
872251881Speter      APR_ARRAY_PUSH(*patterns, const char *) = apr_pstrdup(result_pool,
873251881Speter                                                            ignore);
874251881Speter    }
875251881Speter
876251881Speter  err = svn_wc__db_read_inherited_props(&inherited_props, &props,
877251881Speter                                        db, local_abspath,
878251881Speter                                        SVN_PROP_INHERITABLE_IGNORES,
879251881Speter                                        scratch_pool, scratch_pool);
880251881Speter
881251881Speter  if (err)
882251881Speter    {
883251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
884251881Speter        return svn_error_trace(err);
885251881Speter
886251881Speter      svn_error_clear(err);
887251881Speter      return SVN_NO_ERROR;
888251881Speter    }
889251881Speter
890251881Speter  if (props)
891251881Speter    {
892251881Speter      const svn_string_t *value;
893251881Speter
894251881Speter      value = svn_hash_gets(props, SVN_PROP_IGNORE);
895251881Speter      if (value)
896251881Speter        svn_cstring_split_append(*patterns, value->data, "\n\r", FALSE,
897251881Speter                                 result_pool);
898251881Speter
899251881Speter      value = svn_hash_gets(props, SVN_PROP_INHERITABLE_IGNORES);
900251881Speter      if (value)
901251881Speter        svn_cstring_split_append(*patterns, value->data, "\n\r", FALSE,
902251881Speter                                 result_pool);
903251881Speter    }
904251881Speter
905251881Speter  for (i = 0; i < inherited_props->nelts; i++)
906251881Speter    {
907251881Speter      svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
908251881Speter        inherited_props, i, svn_prop_inherited_item_t *);
909251881Speter      const svn_string_t *value;
910251881Speter
911251881Speter      value = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES);
912251881Speter
913251881Speter      if (value)
914251881Speter        svn_cstring_split_append(*patterns, value->data,
915251881Speter                                 "\n\r", FALSE, result_pool);
916251881Speter    }
917251881Speter
918251881Speter  return SVN_NO_ERROR;
919251881Speter}
920251881Speter
921251881Speter
922251881Speter/* Compare LOCAL_ABSPATH with items in the EXTERNALS hash to see if
923251881Speter   LOCAL_ABSPATH is the drop location for, or an intermediate directory
924251881Speter   of the drop location for, an externals definition.  Use SCRATCH_POOL
925251881Speter   for scratchwork.  */
926251881Speterstatic svn_boolean_t
927251881Speteris_external_path(apr_hash_t *externals,
928251881Speter                 const char *local_abspath,
929251881Speter                 apr_pool_t *scratch_pool)
930251881Speter{
931251881Speter  apr_hash_index_t *hi;
932251881Speter
933251881Speter  /* First try: does the path exist as a key in the hash? */
934251881Speter  if (svn_hash_gets(externals, local_abspath))
935251881Speter    return TRUE;
936251881Speter
937251881Speter  /* Failing that, we need to check if any external is a child of
938251881Speter     LOCAL_ABSPATH.  */
939251881Speter  for (hi = apr_hash_first(scratch_pool, externals);
940251881Speter       hi;
941251881Speter       hi = apr_hash_next(hi))
942251881Speter    {
943251881Speter      const char *external_abspath = svn__apr_hash_index_key(hi);
944251881Speter
945251881Speter      if (svn_dirent_is_child(local_abspath, external_abspath, NULL))
946251881Speter        return TRUE;
947251881Speter    }
948251881Speter
949251881Speter  return FALSE;
950251881Speter}
951251881Speter
952251881Speter
953251881Speter/* Assuming that LOCAL_ABSPATH is unversioned, send a status structure
954251881Speter   for it through STATUS_FUNC/STATUS_BATON unless this path is being
955251881Speter   ignored.  This function should never be called on a versioned entry.
956251881Speter
957251881Speter   LOCAL_ABSPATH is the path to the unversioned file whose status is being
958251881Speter   requested.  PATH_KIND is the node kind of NAME as determined by the
959251881Speter   caller.  PATH_SPECIAL is the special status of the path, also determined
960251881Speter   by the caller.
961251881Speter   PATTERNS points to a list of filename patterns which are marked as ignored.
962251881Speter   None of these parameter may be NULL.
963251881Speter
964251881Speter   If NO_IGNORE is TRUE, the item will be added regardless of
965251881Speter   whether it is ignored; otherwise we will only add the item if it
966251881Speter   does not match any of the patterns in PATTERN or INHERITED_IGNORES.
967251881Speter
968251881Speter   Allocate everything in POOL.
969251881Speter*/
970251881Speterstatic svn_error_t *
971251881Spetersend_unversioned_item(const struct walk_status_baton *wb,
972251881Speter                      const char *local_abspath,
973251881Speter                      const svn_io_dirent2_t *dirent,
974251881Speter                      svn_boolean_t tree_conflicted,
975251881Speter                      const apr_array_header_t *patterns,
976251881Speter                      svn_boolean_t no_ignore,
977251881Speter                      svn_wc_status_func4_t status_func,
978251881Speter                      void *status_baton,
979251881Speter                      apr_pool_t *scratch_pool)
980251881Speter{
981251881Speter  svn_boolean_t is_ignored;
982251881Speter  svn_boolean_t is_external;
983251881Speter  svn_wc_status3_t *status;
984251881Speter  const char *base_name = svn_dirent_basename(local_abspath, NULL);
985251881Speter
986251881Speter  is_ignored = svn_wc_match_ignore_list(base_name, patterns, scratch_pool);
987251881Speter  SVN_ERR(assemble_unversioned(&status,
988251881Speter                               wb->db, local_abspath,
989251881Speter                               dirent, tree_conflicted,
990251881Speter                               is_ignored,
991251881Speter                               scratch_pool, scratch_pool));
992251881Speter
993251881Speter  is_external = is_external_path(wb->externals, local_abspath, scratch_pool);
994251881Speter  if (is_external)
995251881Speter    status->node_status = svn_wc_status_external;
996251881Speter
997251881Speter  /* We can have a tree conflict on an unversioned path, i.e. an incoming
998251881Speter   * delete on a locally deleted path during an update. Don't ever ignore
999251881Speter   * those! */
1000251881Speter  if (status->conflicted)
1001251881Speter    is_ignored = FALSE;
1002251881Speter
1003251881Speter  /* If we aren't ignoring it, or if it's an externals path, pass this
1004251881Speter     entry to the status func. */
1005251881Speter  if (no_ignore
1006251881Speter      || !is_ignored
1007251881Speter      || is_external)
1008251881Speter    return svn_error_trace((*status_func)(status_baton, local_abspath,
1009251881Speter                                          status, scratch_pool));
1010251881Speter
1011251881Speter  return SVN_NO_ERROR;
1012251881Speter}
1013251881Speter
1014251881Speterstatic svn_error_t *
1015251881Speterget_dir_status(const struct walk_status_baton *wb,
1016251881Speter               const char *local_abspath,
1017251881Speter               svn_boolean_t skip_this_dir,
1018251881Speter               const char *parent_repos_root_url,
1019251881Speter               const char *parent_repos_relpath,
1020251881Speter               const char *parent_repos_uuid,
1021251881Speter               const struct svn_wc__db_info_t *dir_info,
1022251881Speter               const svn_io_dirent2_t *dirent,
1023251881Speter               const apr_array_header_t *ignore_patterns,
1024251881Speter               svn_depth_t depth,
1025251881Speter               svn_boolean_t get_all,
1026251881Speter               svn_boolean_t no_ignore,
1027251881Speter               svn_wc_status_func4_t status_func,
1028251881Speter               void *status_baton,
1029251881Speter               svn_cancel_func_t cancel_func,
1030251881Speter               void *cancel_baton,
1031251881Speter               apr_pool_t *scratch_pool);
1032251881Speter
1033251881Speter/* Send out a status structure according to the information gathered on one
1034251881Speter * child node. (Basically this function is the guts of the loop in
1035251881Speter * get_dir_status() and of get_child_status().)
1036251881Speter *
1037251881Speter * Send a status structure of LOCAL_ABSPATH. PARENT_ABSPATH must be the
1038251881Speter * dirname of LOCAL_ABSPATH.
1039251881Speter *
1040251881Speter * INFO should reflect the information on LOCAL_ABSPATH; LOCAL_ABSPATH must
1041251881Speter * be an unversioned file or dir, or a versioned file.  For versioned
1042251881Speter * directories use get_dir_status() instead.
1043251881Speter *
1044251881Speter * INFO may be NULL for an unversioned node. If such node has a tree conflict,
1045251881Speter * UNVERSIONED_TREE_CONFLICTED may be set to TRUE. If INFO is non-NULL,
1046251881Speter * UNVERSIONED_TREE_CONFLICTED is ignored.
1047251881Speter *
1048251881Speter * DIRENT should reflect LOCAL_ABSPATH's dirent information.
1049251881Speter *
1050251881Speter * DIR_REPOS_* should reflect LOCAL_ABSPATH's parent URL, i.e. LOCAL_ABSPATH's
1051251881Speter * URL treated with svn_uri_dirname(). ### TODO verify this (externals)
1052251881Speter *
1053251881Speter * If *COLLECTED_IGNORE_PATTERNS is NULL and ignore patterns are needed in this
1054251881Speter * call, then *COLLECTED_IGNORE_PATTERNS will be set to an apr_array_header_t*
1055251881Speter * containing all ignore patterns, as returned by collect_ignore_patterns() on
1056251881Speter * PARENT_ABSPATH and IGNORE_PATTERNS. If *COLLECTED_IGNORE_PATTERNS is passed
1057251881Speter * non-NULL, it is assumed it already holds those results.
1058251881Speter * This speeds up repeated calls with the same PARENT_ABSPATH.
1059251881Speter *
1060251881Speter * *COLLECTED_IGNORE_PATTERNS will be allocated in RESULT_POOL. All other
1061251881Speter * allocations are made in SCRATCH_POOL.
1062251881Speter *
1063251881Speter * The remaining parameters correspond to get_dir_status(). */
1064251881Speterstatic svn_error_t *
1065251881Speterone_child_status(const struct walk_status_baton *wb,
1066251881Speter                 const char *local_abspath,
1067251881Speter                 const char *parent_abspath,
1068251881Speter                 const struct svn_wc__db_info_t *info,
1069251881Speter                 const svn_io_dirent2_t *dirent,
1070251881Speter                 const char *dir_repos_root_url,
1071251881Speter                 const char *dir_repos_relpath,
1072251881Speter                 const char *dir_repos_uuid,
1073251881Speter                 svn_boolean_t unversioned_tree_conflicted,
1074251881Speter                 apr_array_header_t **collected_ignore_patterns,
1075251881Speter                 const apr_array_header_t *ignore_patterns,
1076251881Speter                 svn_depth_t depth,
1077251881Speter                 svn_boolean_t get_all,
1078251881Speter                 svn_boolean_t no_ignore,
1079251881Speter                 svn_wc_status_func4_t status_func,
1080251881Speter                 void *status_baton,
1081251881Speter                 svn_cancel_func_t cancel_func,
1082251881Speter                 void *cancel_baton,
1083251881Speter                 apr_pool_t *result_pool,
1084251881Speter                 apr_pool_t *scratch_pool)
1085251881Speter{
1086251881Speter  svn_boolean_t conflicted = info ? info->conflicted
1087251881Speter                                  : unversioned_tree_conflicted;
1088251881Speter
1089251881Speter  if (info
1090251881Speter      && info->status != svn_wc__db_status_not_present
1091251881Speter      && info->status != svn_wc__db_status_excluded
1092251881Speter      && info->status != svn_wc__db_status_server_excluded
1093251881Speter      && !(info->kind == svn_node_unknown
1094251881Speter           && info->status == svn_wc__db_status_normal))
1095251881Speter    {
1096251881Speter      if (depth == svn_depth_files
1097251881Speter          && info->kind == svn_node_dir)
1098251881Speter        {
1099251881Speter          return SVN_NO_ERROR;
1100251881Speter        }
1101251881Speter
1102251881Speter      SVN_ERR(send_status_structure(wb, local_abspath,
1103251881Speter                                    dir_repos_root_url,
1104251881Speter                                    dir_repos_relpath,
1105251881Speter                                    dir_repos_uuid,
1106251881Speter                                    info, dirent, get_all,
1107251881Speter                                    status_func, status_baton,
1108251881Speter                                    scratch_pool));
1109251881Speter
1110251881Speter      /* Descend in subdirectories. */
1111251881Speter      if (depth == svn_depth_infinity
1112251881Speter          && info->kind == svn_node_dir)
1113251881Speter        {
1114251881Speter          SVN_ERR(get_dir_status(wb, local_abspath, TRUE,
1115251881Speter                                 dir_repos_root_url, dir_repos_relpath,
1116251881Speter                                 dir_repos_uuid, info,
1117251881Speter                                 dirent, ignore_patterns,
1118251881Speter                                 svn_depth_infinity, get_all,
1119251881Speter                                 no_ignore,
1120251881Speter                                 status_func, status_baton,
1121251881Speter                                 cancel_func, cancel_baton,
1122251881Speter                                 scratch_pool));
1123251881Speter        }
1124251881Speter
1125251881Speter      return SVN_NO_ERROR;
1126251881Speter    }
1127251881Speter
1128251881Speter  /* If conflicted, fall right through to unversioned.
1129251881Speter   * With depth_files, show all conflicts, even if their report is only
1130251881Speter   * about directories. A tree conflict may actually report two different
1131251881Speter   * kinds, so it's not so easy to define what depth=files means. We could go
1132251881Speter   * look up the kinds in the conflict ... just show all. */
1133251881Speter  if (! conflicted)
1134251881Speter    {
1135251881Speter      /* Selected node, but not found */
1136251881Speter      if (dirent == NULL)
1137251881Speter        return SVN_NO_ERROR;
1138251881Speter
1139251881Speter      if (depth == svn_depth_files && dirent->kind == svn_node_dir)
1140251881Speter        return SVN_NO_ERROR;
1141251881Speter
1142251881Speter      if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL),
1143251881Speter                            scratch_pool))
1144251881Speter        return SVN_NO_ERROR;
1145251881Speter    }
1146251881Speter
1147251881Speter  /* The node exists on disk but there is no versioned information about it,
1148251881Speter   * or it doesn't exist but is a tree conflicted path or should be
1149251881Speter   * reported not-present. */
1150251881Speter
1151251881Speter  /* Why pass ignore patterns on a tree conflicted node, even if it should
1152251881Speter   * always show up in clients' status reports anyway? Because the calling
1153251881Speter   * client decides whether to ignore, and thus this flag needs to be
1154251881Speter   * determined.  For example, in 'svn status', plain unversioned nodes show
1155251881Speter   * as '?  C', where ignored ones show as 'I  C'. */
1156251881Speter
1157251881Speter  if (ignore_patterns && ! *collected_ignore_patterns)
1158251881Speter    SVN_ERR(collect_ignore_patterns(collected_ignore_patterns,
1159251881Speter                                    wb->db, parent_abspath, ignore_patterns,
1160251881Speter                                    result_pool, scratch_pool));
1161251881Speter
1162251881Speter  SVN_ERR(send_unversioned_item(wb,
1163251881Speter                                local_abspath,
1164251881Speter                                dirent,
1165251881Speter                                conflicted,
1166251881Speter                                *collected_ignore_patterns,
1167251881Speter                                no_ignore,
1168251881Speter                                status_func, status_baton,
1169251881Speter                                scratch_pool));
1170251881Speter
1171251881Speter  return SVN_NO_ERROR;
1172251881Speter}
1173251881Speter
1174251881Speter/* Send svn_wc_status3_t * structures for the directory LOCAL_ABSPATH and
1175251881Speter   for all its child nodes (according to DEPTH) through STATUS_FUNC /
1176251881Speter   STATUS_BATON.
1177251881Speter
1178251881Speter   If SKIP_THIS_DIR is TRUE, the directory's own status will not be reported.
1179251881Speter   All subdirs reached by recursion will be reported regardless of this
1180251881Speter   parameter's value.
1181251881Speter
1182251881Speter   PARENT_REPOS_* parameters can be set to refer to LOCAL_ABSPATH's parent's
1183251881Speter   URL, i.e. the URL the WC reflects at the dirname of LOCAL_ABSPATH, to avoid
1184251881Speter   retrieving them again. Otherwise they must be NULL.
1185251881Speter
1186251881Speter   DIR_INFO can be set to the information of LOCAL_ABSPATH, to avoid retrieving
1187251881Speter   it again. Otherwise it must be NULL.
1188251881Speter
1189251881Speter   DIRENT is LOCAL_ABSPATH's own dirent and is only needed if it is reported,
1190251881Speter   so if SKIP_THIS_DIR is TRUE, DIRENT can be left NULL.
1191251881Speter
1192251881Speter   Other arguments are the same as those passed to
1193251881Speter   svn_wc_get_status_editor5().  */
1194251881Speterstatic svn_error_t *
1195251881Speterget_dir_status(const struct walk_status_baton *wb,
1196251881Speter               const char *local_abspath,
1197251881Speter               svn_boolean_t skip_this_dir,
1198251881Speter               const char *parent_repos_root_url,
1199251881Speter               const char *parent_repos_relpath,
1200251881Speter               const char *parent_repos_uuid,
1201251881Speter               const struct svn_wc__db_info_t *dir_info,
1202251881Speter               const svn_io_dirent2_t *dirent,
1203251881Speter               const apr_array_header_t *ignore_patterns,
1204251881Speter               svn_depth_t depth,
1205251881Speter               svn_boolean_t get_all,
1206251881Speter               svn_boolean_t no_ignore,
1207251881Speter               svn_wc_status_func4_t status_func,
1208251881Speter               void *status_baton,
1209251881Speter               svn_cancel_func_t cancel_func,
1210251881Speter               void *cancel_baton,
1211251881Speter               apr_pool_t *scratch_pool)
1212251881Speter{
1213251881Speter  const char *dir_repos_root_url;
1214251881Speter  const char *dir_repos_relpath;
1215251881Speter  const char *dir_repos_uuid;
1216251881Speter  apr_hash_t *dirents, *nodes, *conflicts, *all_children;
1217251881Speter  apr_array_header_t *sorted_children;
1218251881Speter  apr_array_header_t *collected_ignore_patterns = NULL;
1219251881Speter  apr_pool_t *iterpool;
1220251881Speter  svn_error_t *err;
1221251881Speter  int i;
1222251881Speter
1223251881Speter  if (cancel_func)
1224251881Speter    SVN_ERR(cancel_func(cancel_baton));
1225251881Speter
1226251881Speter  if (depth == svn_depth_unknown)
1227251881Speter    depth = svn_depth_infinity;
1228251881Speter
1229251881Speter  iterpool = svn_pool_create(scratch_pool);
1230251881Speter
1231251881Speter  err = svn_io_get_dirents3(&dirents, local_abspath, FALSE, scratch_pool,
1232251881Speter                            iterpool);
1233251881Speter  if (err
1234251881Speter      && (APR_STATUS_IS_ENOENT(err->apr_err)
1235251881Speter         || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
1236251881Speter    {
1237251881Speter      svn_error_clear(err);
1238251881Speter      dirents = apr_hash_make(scratch_pool);
1239251881Speter    }
1240251881Speter  else
1241251881Speter    SVN_ERR(err);
1242251881Speter
1243251881Speter  if (!dir_info)
1244269847Speter      SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath,
1245269847Speter                                          scratch_pool, iterpool));
1246251881Speter
1247251881Speter  SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url,
1248251881Speter                                     &dir_repos_uuid, dir_info,
1249251881Speter                                     parent_repos_relpath,
1250251881Speter                                     parent_repos_root_url, parent_repos_uuid,
1251251881Speter                                     wb->db, local_abspath,
1252251881Speter                                     scratch_pool, iterpool));
1253251881Speter
1254251881Speter  /* Create a hash containing all children.  The source hashes
1255251881Speter     don't all map the same types, but only the keys of the result
1256251881Speter     hash are subsequently used. */
1257251881Speter  SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
1258251881Speter                                        wb->db, local_abspath,
1259251881Speter                                        scratch_pool, iterpool));
1260251881Speter
1261251881Speter  all_children = apr_hash_overlay(scratch_pool, nodes, dirents);
1262251881Speter  if (apr_hash_count(conflicts) > 0)
1263251881Speter    all_children = apr_hash_overlay(scratch_pool, conflicts, all_children);
1264251881Speter
1265251881Speter  /* Handle "this-dir" first. */
1266251881Speter  if (! skip_this_dir)
1267251881Speter    {
1268251881Speter      /* This code is not conditional on HAVE_SYMLINK as some systems that do
1269251881Speter         not allow creating symlinks (!HAVE_SYMLINK) can still encounter
1270251881Speter         symlinks (or in case of Windows also 'Junctions') created by other
1271251881Speter         methods.
1272251881Speter
1273251881Speter         Without this block a working copy in the root of a junction is
1274251881Speter         reported as an obstruction, because the junction itself is reported as
1275251881Speter         special.
1276251881Speter
1277251881Speter         Systems that have no symlink support at all, would always see
1278251881Speter         dirent->special as FALSE, so even there enabling this code shouldn't
1279251881Speter         produce problems.
1280251881Speter       */
1281251881Speter      if (dirent->special)
1282251881Speter        {
1283251881Speter          svn_io_dirent2_t *this_dirent = svn_io_dirent2_dup(dirent, iterpool);
1284251881Speter
1285251881Speter          /* We're being pointed to "this-dir" via a symlink.
1286251881Speter           * Get the real node kind and pretend the path is not a symlink.
1287251881Speter           * This prevents send_status_structure() from treating this-dir
1288251881Speter           * as a directory obstructed by a file. */
1289251881Speter          SVN_ERR(svn_io_check_resolved_path(local_abspath,
1290251881Speter                                             &this_dirent->kind, iterpool));
1291251881Speter          this_dirent->special = FALSE;
1292251881Speter          SVN_ERR(send_status_structure(wb, local_abspath,
1293251881Speter                                        parent_repos_root_url,
1294251881Speter                                        parent_repos_relpath,
1295251881Speter                                        parent_repos_uuid,
1296251881Speter                                        dir_info, this_dirent, get_all,
1297251881Speter                                        status_func, status_baton,
1298251881Speter                                        iterpool));
1299251881Speter        }
1300251881Speter     else
1301251881Speter        SVN_ERR(send_status_structure(wb, local_abspath,
1302251881Speter                                      parent_repos_root_url,
1303251881Speter                                      parent_repos_relpath,
1304251881Speter                                      parent_repos_uuid,
1305251881Speter                                      dir_info, dirent, get_all,
1306251881Speter                                      status_func, status_baton,
1307251881Speter                                      iterpool));
1308251881Speter    }
1309251881Speter
1310251881Speter  /* If the requested depth is empty, we only need status on this-dir. */
1311251881Speter  if (depth == svn_depth_empty)
1312251881Speter    return SVN_NO_ERROR;
1313251881Speter
1314251881Speter  /* Walk all the children of this directory. */
1315251881Speter  sorted_children = svn_sort__hash(all_children,
1316251881Speter                                   svn_sort_compare_items_lexically,
1317251881Speter                                   scratch_pool);
1318251881Speter  for (i = 0; i < sorted_children->nelts; i++)
1319251881Speter    {
1320251881Speter      const void *key;
1321251881Speter      apr_ssize_t klen;
1322251881Speter      svn_sort__item_t item;
1323251881Speter      const char *child_abspath;
1324251881Speter      svn_io_dirent2_t *child_dirent;
1325251881Speter      const struct svn_wc__db_info_t *child_info;
1326251881Speter
1327251881Speter      svn_pool_clear(iterpool);
1328251881Speter
1329251881Speter      item = APR_ARRAY_IDX(sorted_children, i, svn_sort__item_t);
1330251881Speter      key = item.key;
1331251881Speter      klen = item.klen;
1332251881Speter
1333251881Speter      child_abspath = svn_dirent_join(local_abspath, key, iterpool);
1334251881Speter      child_dirent = apr_hash_get(dirents, key, klen);
1335251881Speter      child_info = apr_hash_get(nodes, key, klen);
1336251881Speter
1337251881Speter      SVN_ERR(one_child_status(wb,
1338251881Speter                               child_abspath,
1339251881Speter                               local_abspath,
1340251881Speter                               child_info,
1341251881Speter                               child_dirent,
1342251881Speter                               dir_repos_root_url,
1343251881Speter                               dir_repos_relpath,
1344251881Speter                               dir_repos_uuid,
1345251881Speter                               apr_hash_get(conflicts, key, klen) != NULL,
1346251881Speter                               &collected_ignore_patterns,
1347251881Speter                               ignore_patterns,
1348251881Speter                               depth,
1349251881Speter                               get_all,
1350251881Speter                               no_ignore,
1351251881Speter                               status_func,
1352251881Speter                               status_baton,
1353251881Speter                               cancel_func,
1354251881Speter                               cancel_baton,
1355251881Speter                               scratch_pool,
1356251881Speter                               iterpool));
1357251881Speter    }
1358251881Speter
1359251881Speter  /* Destroy our subpools. */
1360251881Speter  svn_pool_destroy(iterpool);
1361251881Speter
1362251881Speter  return SVN_NO_ERROR;
1363251881Speter}
1364251881Speter
1365251881Speter/* Send an svn_wc_status3_t * structure for the versioned file, or for the
1366251881Speter * unversioned file or directory, LOCAL_ABSPATH, which is not ignored (an
1367251881Speter * explicit target). Does not recurse.
1368251881Speter *
1369251881Speter * INFO should reflect LOCAL_ABSPATH's information, but should be NULL for
1370251881Speter * unversioned nodes. An unversioned and tree-conflicted node however should
1371251881Speter * pass a non-NULL INFO as returned by read_info() (INFO->CONFLICTED = TRUE).
1372251881Speter *
1373251881Speter * DIRENT should reflect LOCAL_ABSPATH.
1374251881Speter *
1375251881Speter * All allocations made in SCRATCH_POOL.
1376251881Speter *
1377251881Speter * The remaining parameters correspond to get_dir_status(). */
1378251881Speterstatic svn_error_t *
1379251881Speterget_child_status(const struct walk_status_baton *wb,
1380251881Speter                 const char *local_abspath,
1381251881Speter                 const struct svn_wc__db_info_t *info,
1382251881Speter                 const svn_io_dirent2_t *dirent,
1383251881Speter                 const apr_array_header_t *ignore_patterns,
1384251881Speter                 svn_boolean_t get_all,
1385251881Speter                 svn_wc_status_func4_t status_func,
1386251881Speter                 void *status_baton,
1387251881Speter                 svn_cancel_func_t cancel_func,
1388251881Speter                 void *cancel_baton,
1389251881Speter                 apr_pool_t *scratch_pool)
1390251881Speter{
1391251881Speter  const char *dir_repos_root_url;
1392251881Speter  const char *dir_repos_relpath;
1393251881Speter  const char *dir_repos_uuid;
1394251881Speter  const struct svn_wc__db_info_t *dir_info;
1395251881Speter  apr_array_header_t *collected_ignore_patterns = NULL;
1396251881Speter  const char *parent_abspath = svn_dirent_dirname(local_abspath,
1397251881Speter                                                  scratch_pool);
1398251881Speter
1399251881Speter  if (cancel_func)
1400251881Speter    SVN_ERR(cancel_func(cancel_baton));
1401251881Speter
1402251881Speter  if (dirent->kind == svn_node_none)
1403251881Speter    dirent = NULL;
1404251881Speter
1405269847Speter  SVN_ERR(svn_wc__db_read_single_info(&dir_info,
1406269847Speter                                      wb->db, parent_abspath,
1407269847Speter                                      scratch_pool, scratch_pool));
1408251881Speter
1409251881Speter  SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url,
1410251881Speter                                     &dir_repos_uuid, dir_info,
1411251881Speter                                     NULL, NULL, NULL,
1412251881Speter                                     wb->db, parent_abspath,
1413251881Speter                                     scratch_pool, scratch_pool));
1414251881Speter
1415251881Speter  /* An unversioned node with a tree conflict will see an INFO != NULL here,
1416251881Speter   * in which case the FALSE passed for UNVERSIONED_TREE_CONFLICTED has no
1417251881Speter   * effect and INFO->CONFLICTED counts.
1418251881Speter   * ### Maybe svn_wc__db_read_children_info() and read_info() should be more
1419251881Speter   * ### alike? */
1420251881Speter  SVN_ERR(one_child_status(wb,
1421251881Speter                           local_abspath,
1422251881Speter                           parent_abspath,
1423251881Speter                           info,
1424251881Speter                           dirent,
1425251881Speter                           dir_repos_root_url,
1426251881Speter                           dir_repos_relpath,
1427251881Speter                           dir_repos_uuid,
1428251881Speter                           FALSE, /* unversioned_tree_conflicted */
1429251881Speter                           &collected_ignore_patterns,
1430251881Speter                           ignore_patterns,
1431251881Speter                           svn_depth_empty,
1432251881Speter                           get_all,
1433251881Speter                           TRUE, /* no_ignore. This is an explicit target. */
1434251881Speter                           status_func,
1435251881Speter                           status_baton,
1436251881Speter                           cancel_func,
1437251881Speter                           cancel_baton,
1438251881Speter                           scratch_pool,
1439251881Speter                           scratch_pool));
1440251881Speter  return SVN_NO_ERROR;
1441251881Speter}
1442251881Speter
1443251881Speter
1444251881Speter
1445251881Speter/*** Helpers ***/
1446251881Speter
1447251881Speter/* A faux status callback function for stashing STATUS item in an hash
1448251881Speter   (which is the BATON), keyed on PATH.  This implements the
1449251881Speter   svn_wc_status_func4_t interface. */
1450251881Speterstatic svn_error_t *
1451251881Speterhash_stash(void *baton,
1452251881Speter           const char *path,
1453251881Speter           const svn_wc_status3_t *status,
1454251881Speter           apr_pool_t *scratch_pool)
1455251881Speter{
1456251881Speter  apr_hash_t *stat_hash = baton;
1457251881Speter  apr_pool_t *hash_pool = apr_hash_pool_get(stat_hash);
1458251881Speter  assert(! svn_hash_gets(stat_hash, path));
1459251881Speter  svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path),
1460251881Speter                svn_wc_dup_status3(status, hash_pool));
1461251881Speter
1462251881Speter  return SVN_NO_ERROR;
1463251881Speter}
1464251881Speter
1465251881Speter
1466251881Speter/* Look up the key PATH in BATON->STATII.  IS_DIR_BATON indicates whether
1467251881Speter   baton is a struct *dir_baton or struct *file_baton.  If the value doesn't
1468251881Speter   yet exist, and the REPOS_NODE_STATUS indicates that this is an addition,
1469251881Speter   create a new status struct using the hash's pool.
1470251881Speter
1471251881Speter   If IS_DIR_BATON is true, THIS_DIR_BATON is a *dir_baton cotaining the out
1472251881Speter   of date (ood) information we want to set in BATON.  This is necessary
1473251881Speter   because this function tweaks the status of out-of-date directories
1474251881Speter   (BATON == THIS_DIR_BATON) and out-of-date directories' parents
1475251881Speter   (BATON == THIS_DIR_BATON->parent_baton).  In the latter case THIS_DIR_BATON
1476251881Speter   contains the ood info we want to bubble up to ancestor directories so these
1477251881Speter   accurately reflect the fact they have an ood descendant.
1478251881Speter
1479251881Speter   Merge REPOS_NODE_STATUS, REPOS_TEXT_STATUS and REPOS_PROP_STATUS into the
1480251881Speter   status structure's "network" fields.
1481251881Speter
1482251881Speter   Iff IS_DIR_BATON is true, DELETED_REV is used as follows, otherwise it
1483251881Speter   is ignored:
1484251881Speter
1485251881Speter       If REPOS_NODE_STATUS is svn_wc_status_deleted then DELETED_REV is
1486251881Speter       optionally the revision path was deleted, in all other cases it must
1487251881Speter       be set to SVN_INVALID_REVNUM.  If DELETED_REV is not
1488251881Speter       SVN_INVALID_REVNUM and REPOS_TEXT_STATUS is svn_wc_status_deleted,
1489251881Speter       then use DELETED_REV to set PATH's ood_last_cmt_rev field in BATON.
1490251881Speter       If DELETED_REV is SVN_INVALID_REVNUM and REPOS_NODE_STATUS is
1491251881Speter       svn_wc_status_deleted, set PATH's ood_last_cmt_rev to its parent's
1492251881Speter       ood_last_cmt_rev value - see comment below.
1493251881Speter
1494251881Speter   If a new struct was added, set the repos_lock to REPOS_LOCK. */
1495251881Speterstatic svn_error_t *
1496251881Spetertweak_statushash(void *baton,
1497251881Speter                 void *this_dir_baton,
1498251881Speter                 svn_boolean_t is_dir_baton,
1499251881Speter                 svn_wc__db_t *db,
1500251881Speter                 const char *local_abspath,
1501251881Speter                 enum svn_wc_status_kind repos_node_status,
1502251881Speter                 enum svn_wc_status_kind repos_text_status,
1503251881Speter                 enum svn_wc_status_kind repos_prop_status,
1504251881Speter                 svn_revnum_t deleted_rev,
1505251881Speter                 const svn_lock_t *repos_lock,
1506251881Speter                 apr_pool_t *scratch_pool)
1507251881Speter{
1508251881Speter  svn_wc_status3_t *statstruct;
1509251881Speter  apr_pool_t *pool;
1510251881Speter  apr_hash_t *statushash;
1511251881Speter
1512251881Speter  if (is_dir_baton)
1513251881Speter    statushash = ((struct dir_baton *) baton)->statii;
1514251881Speter  else
1515251881Speter    statushash = ((struct file_baton *) baton)->dir_baton->statii;
1516251881Speter  pool = apr_hash_pool_get(statushash);
1517251881Speter
1518251881Speter  /* Is PATH already a hash-key? */
1519251881Speter  statstruct = svn_hash_gets(statushash, local_abspath);
1520251881Speter
1521251881Speter  /* If not, make it so. */
1522251881Speter  if (! statstruct)
1523251881Speter    {
1524251881Speter      /* If this item isn't being added, then we're most likely
1525251881Speter         dealing with a non-recursive (or at least partially
1526251881Speter         non-recursive) working copy.  Due to bugs in how the client
1527251881Speter         reports the state of non-recursive working copies, the
1528251881Speter         repository can send back responses about paths that don't
1529251881Speter         even exist locally.  Our best course here is just to ignore
1530251881Speter         those responses.  After all, if the client had reported
1531251881Speter         correctly in the first, that path would either be mentioned
1532251881Speter         as an 'add' or not mentioned at all, depending on how we
1533251881Speter         eventually fix the bugs in non-recursivity.  See issue
1534251881Speter         #2122 for details. */
1535251881Speter      if (repos_node_status != svn_wc_status_added)
1536251881Speter        return SVN_NO_ERROR;
1537251881Speter
1538251881Speter      /* Use the public API to get a statstruct, and put it into the hash. */
1539251881Speter      SVN_ERR(internal_status(&statstruct, db, local_abspath, pool,
1540251881Speter                              scratch_pool));
1541251881Speter      statstruct->repos_lock = repos_lock;
1542251881Speter      svn_hash_sets(statushash, apr_pstrdup(pool, local_abspath), statstruct);
1543251881Speter    }
1544251881Speter
1545251881Speter  /* Merge a repos "delete" + "add" into a single "replace". */
1546251881Speter  if ((repos_node_status == svn_wc_status_added)
1547251881Speter      && (statstruct->repos_node_status == svn_wc_status_deleted))
1548251881Speter    repos_node_status = svn_wc_status_replaced;
1549251881Speter
1550251881Speter  /* Tweak the structure's repos fields. */
1551251881Speter  if (repos_node_status)
1552251881Speter    statstruct->repos_node_status = repos_node_status;
1553251881Speter  if (repos_text_status)
1554251881Speter    statstruct->repos_text_status = repos_text_status;
1555251881Speter  if (repos_prop_status)
1556251881Speter    statstruct->repos_prop_status = repos_prop_status;
1557251881Speter
1558251881Speter  /* Copy out-of-date info. */
1559251881Speter  if (is_dir_baton)
1560251881Speter    {
1561251881Speter      struct dir_baton *b = this_dir_baton;
1562251881Speter
1563251881Speter      if (!statstruct->repos_relpath && b->repos_relpath)
1564251881Speter        {
1565251881Speter          if (statstruct->repos_node_status == svn_wc_status_deleted)
1566251881Speter            {
1567251881Speter              /* When deleting PATH, BATON is for PATH's parent,
1568251881Speter                 so we must construct PATH's real statstruct->url. */
1569251881Speter              statstruct->repos_relpath =
1570251881Speter                            svn_relpath_join(b->repos_relpath,
1571251881Speter                                             svn_dirent_basename(local_abspath,
1572251881Speter                                                                 NULL),
1573251881Speter                                             pool);
1574251881Speter            }
1575251881Speter          else
1576251881Speter            statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath);
1577251881Speter
1578251881Speter          statstruct->repos_root_url =
1579251881Speter                              b->edit_baton->anchor_status->repos_root_url;
1580251881Speter          statstruct->repos_uuid =
1581251881Speter                              b->edit_baton->anchor_status->repos_uuid;
1582251881Speter        }
1583251881Speter
1584251881Speter      /* The last committed date, and author for deleted items
1585251881Speter         isn't available. */
1586251881Speter      if (statstruct->repos_node_status == svn_wc_status_deleted)
1587251881Speter        {
1588251881Speter          statstruct->ood_kind = statstruct->kind;
1589251881Speter
1590251881Speter          /* Pre 1.5 servers don't provide the revision a path was deleted.
1591251881Speter             So we punt and use the last committed revision of the path's
1592251881Speter             parent, which has some chance of being correct.  At worse it
1593251881Speter             is a higher revision than the path was deleted, but this is
1594251881Speter             better than nothing... */
1595251881Speter          if (deleted_rev == SVN_INVALID_REVNUM)
1596251881Speter            statstruct->ood_changed_rev =
1597251881Speter              ((struct dir_baton *) baton)->ood_changed_rev;
1598251881Speter          else
1599251881Speter            statstruct->ood_changed_rev = deleted_rev;
1600251881Speter        }
1601251881Speter      else
1602251881Speter        {
1603251881Speter          statstruct->ood_kind = b->ood_kind;
1604251881Speter          statstruct->ood_changed_rev = b->ood_changed_rev;
1605251881Speter          statstruct->ood_changed_date = b->ood_changed_date;
1606251881Speter          if (b->ood_changed_author)
1607251881Speter            statstruct->ood_changed_author =
1608251881Speter              apr_pstrdup(pool, b->ood_changed_author);
1609251881Speter        }
1610251881Speter
1611251881Speter    }
1612251881Speter  else
1613251881Speter    {
1614251881Speter      struct file_baton *b = baton;
1615251881Speter      statstruct->ood_changed_rev = b->ood_changed_rev;
1616251881Speter      statstruct->ood_changed_date = b->ood_changed_date;
1617251881Speter      if (!statstruct->repos_relpath && b->repos_relpath)
1618251881Speter        {
1619251881Speter          statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath);
1620251881Speter          statstruct->repos_root_url =
1621251881Speter                          b->edit_baton->anchor_status->repos_root_url;
1622251881Speter          statstruct->repos_uuid =
1623251881Speter                          b->edit_baton->anchor_status->repos_uuid;
1624251881Speter        }
1625251881Speter      statstruct->ood_kind = b->ood_kind;
1626251881Speter      if (b->ood_changed_author)
1627251881Speter        statstruct->ood_changed_author =
1628251881Speter          apr_pstrdup(pool, b->ood_changed_author);
1629251881Speter    }
1630251881Speter  return SVN_NO_ERROR;
1631251881Speter}
1632251881Speter
1633251881Speter/* Returns the URL for DB */
1634251881Speterstatic const char *
1635251881Speterfind_dir_repos_relpath(const struct dir_baton *db, apr_pool_t *pool)
1636251881Speter{
1637251881Speter  /* If we have no name, we're the root, return the anchor URL. */
1638251881Speter  if (! db->name)
1639251881Speter    return db->edit_baton->anchor_status->repos_relpath;
1640251881Speter  else
1641251881Speter    {
1642251881Speter      const char *repos_relpath;
1643251881Speter      struct dir_baton *pb = db->parent_baton;
1644251881Speter      const svn_wc_status3_t *status = svn_hash_gets(pb->statii,
1645251881Speter                                                     db->local_abspath);
1646251881Speter      /* Note that status->repos_relpath could be NULL in the case of a missing
1647251881Speter       * directory, which means we need to recurse up another level to get
1648251881Speter       * a useful relpath. */
1649251881Speter      if (status && status->repos_relpath)
1650251881Speter        return status->repos_relpath;
1651251881Speter
1652251881Speter      repos_relpath = find_dir_repos_relpath(pb, pool);
1653251881Speter      return svn_relpath_join(repos_relpath, db->name, pool);
1654251881Speter    }
1655251881Speter}
1656251881Speter
1657251881Speter
1658251881Speter
1659251881Speter/* Create a new dir_baton for subdir PATH. */
1660251881Speterstatic svn_error_t *
1661251881Spetermake_dir_baton(void **dir_baton,
1662251881Speter               const char *path,
1663251881Speter               struct edit_baton *edit_baton,
1664251881Speter               struct dir_baton *parent_baton,
1665251881Speter               apr_pool_t *result_pool)
1666251881Speter{
1667251881Speter  struct dir_baton *pb = parent_baton;
1668251881Speter  struct edit_baton *eb = edit_baton;
1669251881Speter  struct dir_baton *d;
1670251881Speter  const char *local_abspath;
1671251881Speter  const svn_wc_status3_t *status_in_parent;
1672251881Speter  apr_pool_t *dir_pool;
1673251881Speter
1674251881Speter  if (parent_baton)
1675251881Speter    dir_pool = svn_pool_create(parent_baton->pool);
1676251881Speter  else
1677251881Speter    dir_pool = svn_pool_create(result_pool);
1678251881Speter
1679251881Speter  d = apr_pcalloc(dir_pool, sizeof(*d));
1680251881Speter
1681251881Speter  SVN_ERR_ASSERT(path || (! pb));
1682251881Speter
1683251881Speter  /* Construct the absolute path of this directory. */
1684251881Speter  if (pb)
1685251881Speter    local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
1686251881Speter  else
1687251881Speter    local_abspath = eb->anchor_abspath;
1688251881Speter
1689251881Speter  /* Finish populating the baton members. */
1690251881Speter  d->pool = dir_pool;
1691251881Speter  d->local_abspath = local_abspath;
1692251881Speter  d->name = path ? svn_dirent_basename(path, dir_pool) : NULL;
1693251881Speter  d->edit_baton = edit_baton;
1694251881Speter  d->parent_baton = parent_baton;
1695251881Speter  d->statii = apr_hash_make(dir_pool);
1696251881Speter  d->ood_changed_rev = SVN_INVALID_REVNUM;
1697251881Speter  d->ood_changed_date = 0;
1698251881Speter  d->repos_relpath = find_dir_repos_relpath(d, dir_pool);
1699251881Speter  d->ood_kind = svn_node_dir;
1700251881Speter  d->ood_changed_author = NULL;
1701251881Speter
1702251881Speter  if (pb)
1703251881Speter    {
1704251881Speter      if (pb->excluded)
1705251881Speter        d->excluded = TRUE;
1706251881Speter      else if (pb->depth == svn_depth_immediates)
1707251881Speter        d->depth = svn_depth_empty;
1708251881Speter      else if (pb->depth == svn_depth_files || pb->depth == svn_depth_empty)
1709251881Speter        d->excluded = TRUE;
1710251881Speter      else if (pb->depth == svn_depth_unknown)
1711251881Speter        /* This is only tentative, it can be overridden from d's entry
1712251881Speter           later. */
1713251881Speter        d->depth = svn_depth_unknown;
1714251881Speter      else
1715251881Speter        d->depth = svn_depth_infinity;
1716251881Speter    }
1717251881Speter  else
1718251881Speter    {
1719251881Speter      d->depth = eb->default_depth;
1720251881Speter    }
1721251881Speter
1722251881Speter  /* Get the status for this path's children.  Of course, we only want
1723251881Speter     to do this if the path is versioned as a directory. */
1724251881Speter  if (pb)
1725251881Speter    status_in_parent = svn_hash_gets(pb->statii, d->local_abspath);
1726251881Speter  else
1727251881Speter    status_in_parent = eb->anchor_status;
1728251881Speter
1729251881Speter  if (status_in_parent
1730251881Speter      && status_in_parent->versioned
1731251881Speter      && (status_in_parent->kind == svn_node_dir)
1732251881Speter      && (! d->excluded)
1733251881Speter      && (d->depth == svn_depth_unknown
1734251881Speter          || d->depth == svn_depth_infinity
1735251881Speter          || d->depth == svn_depth_files
1736251881Speter          || d->depth == svn_depth_immediates)
1737251881Speter          )
1738251881Speter    {
1739251881Speter      const svn_wc_status3_t *this_dir_status;
1740251881Speter      const apr_array_header_t *ignores = eb->ignores;
1741251881Speter
1742251881Speter      SVN_ERR(get_dir_status(&eb->wb, local_abspath, TRUE,
1743251881Speter                             status_in_parent->repos_root_url,
1744251881Speter                             NULL /*parent_repos_relpath*/,
1745251881Speter                             status_in_parent->repos_uuid,
1746251881Speter                             NULL,
1747251881Speter                             NULL /* dirent */, ignores,
1748251881Speter                             d->depth == svn_depth_files
1749251881Speter                                      ? svn_depth_files
1750251881Speter                                      : svn_depth_immediates,
1751251881Speter                             TRUE, TRUE,
1752251881Speter                             hash_stash, d->statii,
1753251881Speter                             eb->cancel_func, eb->cancel_baton,
1754251881Speter                             dir_pool));
1755251881Speter
1756251881Speter      /* If we found a depth here, it should govern. */
1757251881Speter      this_dir_status = svn_hash_gets(d->statii, d->local_abspath);
1758251881Speter      if (this_dir_status && this_dir_status->versioned
1759251881Speter          && (d->depth == svn_depth_unknown
1760251881Speter              || d->depth > status_in_parent->depth))
1761251881Speter        {
1762251881Speter          d->depth = this_dir_status->depth;
1763251881Speter        }
1764251881Speter    }
1765251881Speter
1766251881Speter  *dir_baton = d;
1767251881Speter  return SVN_NO_ERROR;
1768251881Speter}
1769251881Speter
1770251881Speter
1771251881Speter/* Make a file baton, using a new subpool of PARENT_DIR_BATON's pool.
1772251881Speter   NAME is just one component, not a path. */
1773251881Speterstatic struct file_baton *
1774251881Spetermake_file_baton(struct dir_baton *parent_dir_baton,
1775251881Speter                const char *path,
1776251881Speter                apr_pool_t *pool)
1777251881Speter{
1778251881Speter  struct dir_baton *pb = parent_dir_baton;
1779251881Speter  struct edit_baton *eb = pb->edit_baton;
1780251881Speter  struct file_baton *f = apr_pcalloc(pool, sizeof(*f));
1781251881Speter
1782251881Speter  /* Finish populating the baton members. */
1783251881Speter  f->local_abspath = svn_dirent_join(eb->anchor_abspath, path, pool);
1784251881Speter  f->name = svn_dirent_basename(f->local_abspath, NULL);
1785251881Speter  f->pool = pool;
1786251881Speter  f->dir_baton = pb;
1787251881Speter  f->edit_baton = eb;
1788251881Speter  f->ood_changed_rev = SVN_INVALID_REVNUM;
1789251881Speter  f->ood_changed_date = 0;
1790251881Speter  f->repos_relpath = svn_relpath_join(find_dir_repos_relpath(pb, pool),
1791251881Speter                                      f->name, pool);
1792251881Speter  f->ood_kind = svn_node_file;
1793251881Speter  f->ood_changed_author = NULL;
1794251881Speter  return f;
1795251881Speter}
1796251881Speter
1797251881Speter
1798251881Speter/**
1799251881Speter * Return a boolean answer to the question "Is @a status something that
1800251881Speter * should be reported?".  @a no_ignore and @a get_all are the same as
1801251881Speter * svn_wc_get_status_editor4().
1802251881Speter */
1803251881Speterstatic svn_boolean_t
1804251881Speteris_sendable_status(const svn_wc_status3_t *status,
1805251881Speter                   svn_boolean_t no_ignore,
1806251881Speter                   svn_boolean_t get_all)
1807251881Speter{
1808251881Speter  /* If the repository status was touched at all, it's interesting. */
1809251881Speter  if (status->repos_node_status != svn_wc_status_none)
1810251881Speter    return TRUE;
1811251881Speter
1812251881Speter  /* If there is a lock in the repository, send it. */
1813251881Speter  if (status->repos_lock)
1814251881Speter    return TRUE;
1815251881Speter
1816251881Speter  if (status->conflicted)
1817251881Speter    return TRUE;
1818251881Speter
1819251881Speter  /* If the item is ignored, and we don't want ignores, skip it. */
1820251881Speter  if ((status->node_status == svn_wc_status_ignored) && (! no_ignore))
1821251881Speter    return FALSE;
1822251881Speter
1823251881Speter  /* If we want everything, we obviously want this single-item subset
1824251881Speter     of everything. */
1825251881Speter  if (get_all)
1826251881Speter    return TRUE;
1827251881Speter
1828251881Speter  /* If the item is unversioned, display it. */
1829251881Speter  if (status->node_status == svn_wc_status_unversioned)
1830251881Speter    return TRUE;
1831251881Speter
1832251881Speter  /* If the text, property or tree state is interesting, send it. */
1833251881Speter  if ((status->node_status != svn_wc_status_none
1834251881Speter       && (status->node_status != svn_wc_status_normal)))
1835251881Speter    return TRUE;
1836251881Speter
1837251881Speter  /* If it's switched, send it. */
1838251881Speter  if (status->switched)
1839251881Speter    return TRUE;
1840251881Speter
1841251881Speter  /* If there is a lock token, send it. */
1842251881Speter  if (status->versioned && status->lock)
1843251881Speter    return TRUE;
1844251881Speter
1845251881Speter  /* If the entry is associated with a changelist, send it. */
1846251881Speter  if (status->changelist)
1847251881Speter    return TRUE;
1848251881Speter
1849251881Speter  /* Otherwise, don't send it. */
1850251881Speter  return FALSE;
1851251881Speter}
1852251881Speter
1853251881Speter
1854251881Speter/* Baton for mark_status. */
1855251881Speterstruct status_baton
1856251881Speter{
1857251881Speter  svn_wc_status_func4_t real_status_func;  /* real status function */
1858251881Speter  void *real_status_baton;                 /* real status baton */
1859251881Speter};
1860251881Speter
1861251881Speter/* A status callback function which wraps the *real* status
1862251881Speter   function/baton.   It simply sets the "repos_node_status" field of the
1863251881Speter   STATUS to svn_wc_status_deleted and passes it off to the real
1864251881Speter   status func/baton. Implements svn_wc_status_func4_t */
1865251881Speterstatic svn_error_t *
1866251881Spetermark_deleted(void *baton,
1867251881Speter             const char *local_abspath,
1868251881Speter             const svn_wc_status3_t *status,
1869251881Speter             apr_pool_t *scratch_pool)
1870251881Speter{
1871251881Speter  struct status_baton *sb = baton;
1872251881Speter  svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool);
1873251881Speter  new_status->repos_node_status = svn_wc_status_deleted;
1874251881Speter  return sb->real_status_func(sb->real_status_baton, local_abspath,
1875251881Speter                              new_status, scratch_pool);
1876251881Speter}
1877251881Speter
1878251881Speter
1879251881Speter/* Handle a directory's STATII hash.  EB is the edit baton.  DIR_PATH
1880251881Speter   and DIR_ENTRY are the on-disk path and entry, respectively, for the
1881251881Speter   directory itself.  Descend into subdirectories according to DEPTH.
1882251881Speter   Also, if DIR_WAS_DELETED is set, each status that is reported
1883251881Speter   through this function will have its repos_text_status field showing
1884251881Speter   a deletion.  Use POOL for all allocations. */
1885251881Speterstatic svn_error_t *
1886251881Speterhandle_statii(struct edit_baton *eb,
1887251881Speter              const char *dir_repos_root_url,
1888251881Speter              const char *dir_repos_relpath,
1889251881Speter              const char *dir_repos_uuid,
1890251881Speter              apr_hash_t *statii,
1891251881Speter              svn_boolean_t dir_was_deleted,
1892251881Speter              svn_depth_t depth,
1893251881Speter              apr_pool_t *pool)
1894251881Speter{
1895251881Speter  const apr_array_header_t *ignores = eb->ignores;
1896251881Speter  apr_hash_index_t *hi;
1897251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
1898251881Speter  svn_wc_status_func4_t status_func = eb->status_func;
1899251881Speter  void *status_baton = eb->status_baton;
1900251881Speter  struct status_baton sb;
1901251881Speter
1902251881Speter  if (dir_was_deleted)
1903251881Speter    {
1904251881Speter      sb.real_status_func = eb->status_func;
1905251881Speter      sb.real_status_baton = eb->status_baton;
1906251881Speter      status_func = mark_deleted;
1907251881Speter      status_baton = &sb;
1908251881Speter    }
1909251881Speter
1910251881Speter  /* Loop over all the statii still in our hash, handling each one. */
1911251881Speter  for (hi = apr_hash_first(pool, statii); hi; hi = apr_hash_next(hi))
1912251881Speter    {
1913251881Speter      const char *local_abspath = svn__apr_hash_index_key(hi);
1914251881Speter      svn_wc_status3_t *status = svn__apr_hash_index_val(hi);
1915251881Speter
1916251881Speter      /* Clear the subpool. */
1917251881Speter      svn_pool_clear(iterpool);
1918251881Speter
1919251881Speter      /* Now, handle the status.  We don't recurse for svn_depth_immediates
1920251881Speter         because we already have the subdirectories' statii. */
1921251881Speter      if (status->versioned && status->kind == svn_node_dir
1922251881Speter          && (depth == svn_depth_unknown
1923251881Speter              || depth == svn_depth_infinity))
1924251881Speter        {
1925251881Speter          SVN_ERR(get_dir_status(&eb->wb,
1926251881Speter                                 local_abspath, TRUE,
1927251881Speter                                 dir_repos_root_url, dir_repos_relpath,
1928251881Speter                                 dir_repos_uuid,
1929251881Speter                                 NULL,
1930251881Speter                                 NULL /* dirent */,
1931251881Speter                                 ignores, depth, eb->get_all, eb->no_ignore,
1932251881Speter                                 status_func, status_baton,
1933251881Speter                                 eb->cancel_func, eb->cancel_baton,
1934251881Speter                                 iterpool));
1935251881Speter        }
1936251881Speter      if (dir_was_deleted)
1937251881Speter        status->repos_node_status = svn_wc_status_deleted;
1938251881Speter      if (is_sendable_status(status, eb->no_ignore, eb->get_all))
1939251881Speter        SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, status,
1940251881Speter                                  iterpool));
1941251881Speter    }
1942251881Speter
1943251881Speter  /* Destroy the subpool. */
1944251881Speter  svn_pool_destroy(iterpool);
1945251881Speter
1946251881Speter  return SVN_NO_ERROR;
1947251881Speter}
1948251881Speter
1949251881Speter
1950251881Speter/*----------------------------------------------------------------------*/
1951251881Speter
1952251881Speter/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1953251881Speter
1954251881Speter/* An svn_delta_editor_t function. */
1955251881Speterstatic svn_error_t *
1956251881Speterset_target_revision(void *edit_baton,
1957251881Speter                    svn_revnum_t target_revision,
1958251881Speter                    apr_pool_t *pool)
1959251881Speter{
1960251881Speter  struct edit_baton *eb = edit_baton;
1961251881Speter  *(eb->target_revision) = target_revision;
1962251881Speter  return SVN_NO_ERROR;
1963251881Speter}
1964251881Speter
1965251881Speter
1966251881Speter/* An svn_delta_editor_t function. */
1967251881Speterstatic svn_error_t *
1968251881Speteropen_root(void *edit_baton,
1969251881Speter          svn_revnum_t base_revision,
1970251881Speter          apr_pool_t *pool,
1971251881Speter          void **dir_baton)
1972251881Speter{
1973251881Speter  struct edit_baton *eb = edit_baton;
1974251881Speter  eb->root_opened = TRUE;
1975251881Speter  return make_dir_baton(dir_baton, NULL, eb, NULL, pool);
1976251881Speter}
1977251881Speter
1978251881Speter
1979251881Speter/* An svn_delta_editor_t function. */
1980251881Speterstatic svn_error_t *
1981251881Speterdelete_entry(const char *path,
1982251881Speter             svn_revnum_t revision,
1983251881Speter             void *parent_baton,
1984251881Speter             apr_pool_t *pool)
1985251881Speter{
1986251881Speter  struct dir_baton *db = parent_baton;
1987251881Speter  struct edit_baton *eb = db->edit_baton;
1988251881Speter  const char *local_abspath = svn_dirent_join(eb->anchor_abspath, path, pool);
1989251881Speter
1990251881Speter  /* Note:  when something is deleted, it's okay to tweak the
1991251881Speter     statushash immediately.  No need to wait until close_file or
1992251881Speter     close_dir, because there's no risk of having to honor the 'added'
1993251881Speter     flag.  We already know this item exists in the working copy. */
1994251881Speter  SVN_ERR(tweak_statushash(db, db, TRUE, eb->db,
1995251881Speter                           local_abspath,
1996251881Speter                           svn_wc_status_deleted, 0, 0, revision, NULL, pool));
1997251881Speter
1998251881Speter  /* Mark the parent dir -- it lost an entry (unless that parent dir
1999251881Speter     is the root node and we're not supposed to report on the root
2000251881Speter     node).  */
2001251881Speter  if (db->parent_baton && (! *eb->target_basename))
2002251881Speter    SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE,eb->db,
2003251881Speter                             db->local_abspath,
2004251881Speter                             svn_wc_status_modified, svn_wc_status_modified,
2005251881Speter                             0, SVN_INVALID_REVNUM, NULL, pool));
2006251881Speter
2007251881Speter  return SVN_NO_ERROR;
2008251881Speter}
2009251881Speter
2010251881Speter
2011251881Speter/* An svn_delta_editor_t function. */
2012251881Speterstatic svn_error_t *
2013251881Speteradd_directory(const char *path,
2014251881Speter              void *parent_baton,
2015251881Speter              const char *copyfrom_path,
2016251881Speter              svn_revnum_t copyfrom_revision,
2017251881Speter              apr_pool_t *pool,
2018251881Speter              void **child_baton)
2019251881Speter{
2020251881Speter  struct dir_baton *pb = parent_baton;
2021251881Speter  struct edit_baton *eb = pb->edit_baton;
2022251881Speter  struct dir_baton *new_db;
2023251881Speter
2024251881Speter  SVN_ERR(make_dir_baton(child_baton, path, eb, pb, pool));
2025251881Speter
2026251881Speter  /* Make this dir as added. */
2027251881Speter  new_db = *child_baton;
2028251881Speter  new_db->added = TRUE;
2029251881Speter
2030251881Speter  /* Mark the parent as changed;  it gained an entry. */
2031251881Speter  pb->text_changed = TRUE;
2032251881Speter
2033251881Speter  return SVN_NO_ERROR;
2034251881Speter}
2035251881Speter
2036251881Speter
2037251881Speter/* An svn_delta_editor_t function. */
2038251881Speterstatic svn_error_t *
2039251881Speteropen_directory(const char *path,
2040251881Speter               void *parent_baton,
2041251881Speter               svn_revnum_t base_revision,
2042251881Speter               apr_pool_t *pool,
2043251881Speter               void **child_baton)
2044251881Speter{
2045251881Speter  struct dir_baton *pb = parent_baton;
2046251881Speter  return make_dir_baton(child_baton, path, pb->edit_baton, pb, pool);
2047251881Speter}
2048251881Speter
2049251881Speter
2050251881Speter/* An svn_delta_editor_t function. */
2051251881Speterstatic svn_error_t *
2052251881Speterchange_dir_prop(void *dir_baton,
2053251881Speter                const char *name,
2054251881Speter                const svn_string_t *value,
2055251881Speter                apr_pool_t *pool)
2056251881Speter{
2057251881Speter  struct dir_baton *db = dir_baton;
2058251881Speter  if (svn_wc_is_normal_prop(name))
2059251881Speter    db->prop_changed = TRUE;
2060251881Speter
2061251881Speter  /* Note any changes to the repository. */
2062251881Speter  if (value != NULL)
2063251881Speter    {
2064251881Speter      if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_REV) == 0)
2065251881Speter        db->ood_changed_rev = SVN_STR_TO_REV(value->data);
2066251881Speter      else if (strcmp(name, SVN_PROP_ENTRY_LAST_AUTHOR) == 0)
2067251881Speter        db->ood_changed_author = apr_pstrdup(db->pool, value->data);
2068251881Speter      else if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0)
2069251881Speter        {
2070251881Speter          apr_time_t tm;
2071251881Speter          SVN_ERR(svn_time_from_cstring(&tm, value->data, db->pool));
2072251881Speter          db->ood_changed_date = tm;
2073251881Speter        }
2074251881Speter    }
2075251881Speter
2076251881Speter  return SVN_NO_ERROR;
2077251881Speter}
2078251881Speter
2079251881Speter
2080251881Speter
2081251881Speter/* An svn_delta_editor_t function. */
2082251881Speterstatic svn_error_t *
2083251881Speterclose_directory(void *dir_baton,
2084251881Speter                apr_pool_t *pool)
2085251881Speter{
2086251881Speter  struct dir_baton *db = dir_baton;
2087251881Speter  struct dir_baton *pb = db->parent_baton;
2088251881Speter  struct edit_baton *eb = db->edit_baton;
2089251881Speter  apr_pool_t *scratch_pool = db->pool;
2090251881Speter
2091251881Speter  /* If nothing has changed and directory has no out of
2092251881Speter     date descendants, return. */
2093251881Speter  if (db->added || db->prop_changed || db->text_changed
2094251881Speter      || db->ood_changed_rev != SVN_INVALID_REVNUM)
2095251881Speter    {
2096251881Speter      enum svn_wc_status_kind repos_node_status;
2097251881Speter      enum svn_wc_status_kind repos_text_status;
2098251881Speter      enum svn_wc_status_kind repos_prop_status;
2099251881Speter
2100251881Speter      /* If this is a new directory, add it to the statushash. */
2101251881Speter      if (db->added)
2102251881Speter        {
2103251881Speter          repos_node_status = svn_wc_status_added;
2104251881Speter          repos_text_status = svn_wc_status_none;
2105251881Speter          repos_prop_status = db->prop_changed ? svn_wc_status_added
2106251881Speter                              : svn_wc_status_none;
2107251881Speter        }
2108251881Speter      else
2109251881Speter        {
2110251881Speter          repos_node_status = (db->text_changed || db->prop_changed)
2111251881Speter                                       ? svn_wc_status_modified
2112251881Speter                                       : svn_wc_status_none;
2113251881Speter          repos_text_status = db->text_changed ? svn_wc_status_modified
2114251881Speter                              : svn_wc_status_none;
2115251881Speter          repos_prop_status = db->prop_changed ? svn_wc_status_modified
2116251881Speter                              : svn_wc_status_none;
2117251881Speter        }
2118251881Speter
2119251881Speter      /* Maybe add this directory to its parent's status hash.  Note
2120251881Speter         that tweak_statushash won't do anything if repos_text_status
2121251881Speter         is not svn_wc_status_added. */
2122251881Speter      if (pb)
2123251881Speter        {
2124251881Speter          /* ### When we add directory locking, we need to find a
2125251881Speter             ### directory lock here. */
2126251881Speter          SVN_ERR(tweak_statushash(pb, db, TRUE, eb->db, db->local_abspath,
2127251881Speter                                   repos_node_status, repos_text_status,
2128251881Speter                                   repos_prop_status, SVN_INVALID_REVNUM, NULL,
2129251881Speter                                   scratch_pool));
2130251881Speter        }
2131251881Speter      else
2132251881Speter        {
2133251881Speter          /* We're editing the root dir of the WC.  As its repos
2134251881Speter             status info isn't otherwise set, set it directly to
2135251881Speter             trigger invocation of the status callback below. */
2136251881Speter          eb->anchor_status->repos_node_status = repos_node_status;
2137251881Speter          eb->anchor_status->repos_prop_status = repos_prop_status;
2138251881Speter          eb->anchor_status->repos_text_status = repos_text_status;
2139251881Speter
2140251881Speter          /* If the root dir is out of date set the ood info directly too. */
2141251881Speter          if (db->ood_changed_rev != eb->anchor_status->revision)
2142251881Speter            {
2143251881Speter              eb->anchor_status->ood_changed_rev = db->ood_changed_rev;
2144251881Speter              eb->anchor_status->ood_changed_date = db->ood_changed_date;
2145251881Speter              eb->anchor_status->ood_kind = db->ood_kind;
2146251881Speter              eb->anchor_status->ood_changed_author =
2147251881Speter                apr_pstrdup(pool, db->ood_changed_author);
2148251881Speter            }
2149251881Speter        }
2150251881Speter    }
2151251881Speter
2152251881Speter  /* Handle this directory's statuses, and then note in the parent
2153251881Speter     that this has been done. */
2154251881Speter  if (pb && ! db->excluded)
2155251881Speter    {
2156251881Speter      svn_boolean_t was_deleted = FALSE;
2157251881Speter      const svn_wc_status3_t *dir_status;
2158251881Speter
2159251881Speter      /* See if the directory was deleted or replaced. */
2160251881Speter      dir_status = svn_hash_gets(pb->statii, db->local_abspath);
2161251881Speter      if (dir_status &&
2162251881Speter          ((dir_status->repos_node_status == svn_wc_status_deleted)
2163251881Speter           || (dir_status->repos_node_status == svn_wc_status_replaced)))
2164251881Speter        was_deleted = TRUE;
2165251881Speter
2166251881Speter      /* Now do the status reporting. */
2167251881Speter      SVN_ERR(handle_statii(eb,
2168251881Speter                            dir_status ? dir_status->repos_root_url : NULL,
2169251881Speter                            dir_status ? dir_status->repos_relpath : NULL,
2170251881Speter                            dir_status ? dir_status->repos_uuid : NULL,
2171251881Speter                            db->statii, was_deleted, db->depth, scratch_pool));
2172251881Speter      if (dir_status && is_sendable_status(dir_status, eb->no_ignore,
2173251881Speter                                           eb->get_all))
2174251881Speter        SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath,
2175251881Speter                                  dir_status, scratch_pool));
2176251881Speter      svn_hash_sets(pb->statii, db->local_abspath, NULL);
2177251881Speter    }
2178251881Speter  else if (! pb)
2179251881Speter    {
2180251881Speter      /* If this is the top-most directory, and the operation had a
2181251881Speter         target, we should only report the target. */
2182251881Speter      if (*eb->target_basename)
2183251881Speter        {
2184251881Speter          const svn_wc_status3_t *tgt_status;
2185251881Speter
2186251881Speter          tgt_status = svn_hash_gets(db->statii, eb->target_abspath);
2187251881Speter          if (tgt_status)
2188251881Speter            {
2189251881Speter              if (tgt_status->versioned
2190251881Speter                  && tgt_status->kind == svn_node_dir)
2191251881Speter                {
2192251881Speter                  SVN_ERR(get_dir_status(&eb->wb,
2193251881Speter                                         eb->target_abspath, TRUE,
2194251881Speter                                         NULL, NULL, NULL, NULL,
2195251881Speter                                         NULL /* dirent */,
2196251881Speter                                         eb->ignores,
2197251881Speter                                         eb->default_depth,
2198251881Speter                                         eb->get_all, eb->no_ignore,
2199251881Speter                                         eb->status_func, eb->status_baton,
2200251881Speter                                         eb->cancel_func, eb->cancel_baton,
2201251881Speter                                         scratch_pool));
2202251881Speter                }
2203251881Speter              if (is_sendable_status(tgt_status, eb->no_ignore, eb->get_all))
2204251881Speter                SVN_ERR((eb->status_func)(eb->status_baton, eb->target_abspath,
2205251881Speter                                          tgt_status, scratch_pool));
2206251881Speter            }
2207251881Speter        }
2208251881Speter      else
2209251881Speter        {
2210251881Speter          /* Otherwise, we report on all our children and ourself.
2211251881Speter             Note that our directory couldn't have been deleted,
2212251881Speter             because it is the root of the edit drive. */
2213251881Speter          SVN_ERR(handle_statii(eb,
2214251881Speter                                eb->anchor_status->repos_root_url,
2215251881Speter                                eb->anchor_status->repos_relpath,
2216251881Speter                                eb->anchor_status->repos_uuid,
2217251881Speter                                db->statii, FALSE, eb->default_depth,
2218251881Speter                                scratch_pool));
2219251881Speter          if (is_sendable_status(eb->anchor_status, eb->no_ignore,
2220251881Speter                                 eb->get_all))
2221251881Speter            SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath,
2222251881Speter                                      eb->anchor_status, scratch_pool));
2223251881Speter          eb->anchor_status = NULL;
2224251881Speter        }
2225251881Speter    }
2226251881Speter
2227251881Speter  svn_pool_clear(scratch_pool); /* Clear baton and its pool */
2228251881Speter
2229251881Speter  return SVN_NO_ERROR;
2230251881Speter}
2231251881Speter
2232251881Speter
2233251881Speter
2234251881Speter/* An svn_delta_editor_t function. */
2235251881Speterstatic svn_error_t *
2236251881Speteradd_file(const char *path,
2237251881Speter         void *parent_baton,
2238251881Speter         const char *copyfrom_path,
2239251881Speter         svn_revnum_t copyfrom_revision,
2240251881Speter         apr_pool_t *pool,
2241251881Speter         void **file_baton)
2242251881Speter{
2243251881Speter  struct dir_baton *pb = parent_baton;
2244251881Speter  struct file_baton *new_fb = make_file_baton(pb, path, pool);
2245251881Speter
2246251881Speter  /* Mark parent dir as changed */
2247251881Speter  pb->text_changed = TRUE;
2248251881Speter
2249251881Speter  /* Make this file as added. */
2250251881Speter  new_fb->added = TRUE;
2251251881Speter
2252251881Speter  *file_baton = new_fb;
2253251881Speter  return SVN_NO_ERROR;
2254251881Speter}
2255251881Speter
2256251881Speter
2257251881Speter/* An svn_delta_editor_t function. */
2258251881Speterstatic svn_error_t *
2259251881Speteropen_file(const char *path,
2260251881Speter          void *parent_baton,
2261251881Speter          svn_revnum_t base_revision,
2262251881Speter          apr_pool_t *pool,
2263251881Speter          void **file_baton)
2264251881Speter{
2265251881Speter  struct dir_baton *pb = parent_baton;
2266251881Speter  struct file_baton *new_fb = make_file_baton(pb, path, pool);
2267251881Speter
2268251881Speter  *file_baton = new_fb;
2269251881Speter  return SVN_NO_ERROR;
2270251881Speter}
2271251881Speter
2272251881Speter
2273251881Speter/* An svn_delta_editor_t function. */
2274251881Speterstatic svn_error_t *
2275251881Speterapply_textdelta(void *file_baton,
2276251881Speter                const char *base_checksum,
2277251881Speter                apr_pool_t *pool,
2278251881Speter                svn_txdelta_window_handler_t *handler,
2279251881Speter                void **handler_baton)
2280251881Speter{
2281251881Speter  struct file_baton *fb = file_baton;
2282251881Speter
2283251881Speter  /* Mark file as having textual mods. */
2284251881Speter  fb->text_changed = TRUE;
2285251881Speter
2286251881Speter  /* Send back a NULL window handler -- we don't need the actual diffs. */
2287251881Speter  *handler_baton = NULL;
2288251881Speter  *handler = svn_delta_noop_window_handler;
2289251881Speter
2290251881Speter  return SVN_NO_ERROR;
2291251881Speter}
2292251881Speter
2293251881Speter
2294251881Speter/* An svn_delta_editor_t function. */
2295251881Speterstatic svn_error_t *
2296251881Speterchange_file_prop(void *file_baton,
2297251881Speter                 const char *name,
2298251881Speter                 const svn_string_t *value,
2299251881Speter                 apr_pool_t *pool)
2300251881Speter{
2301251881Speter  struct file_baton *fb = file_baton;
2302251881Speter  if (svn_wc_is_normal_prop(name))
2303251881Speter    fb->prop_changed = TRUE;
2304251881Speter
2305251881Speter  /* Note any changes to the repository. */
2306251881Speter  if (value != NULL)
2307251881Speter    {
2308251881Speter      if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_REV) == 0)
2309251881Speter        fb->ood_changed_rev = SVN_STR_TO_REV(value->data);
2310251881Speter      else if (strcmp(name, SVN_PROP_ENTRY_LAST_AUTHOR) == 0)
2311251881Speter        fb->ood_changed_author = apr_pstrdup(fb->dir_baton->pool,
2312251881Speter                                              value->data);
2313251881Speter      else if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0)
2314251881Speter        {
2315251881Speter          apr_time_t tm;
2316251881Speter          SVN_ERR(svn_time_from_cstring(&tm, value->data,
2317251881Speter                                        fb->dir_baton->pool));
2318251881Speter          fb->ood_changed_date = tm;
2319251881Speter        }
2320251881Speter    }
2321251881Speter
2322251881Speter  return SVN_NO_ERROR;
2323251881Speter}
2324251881Speter
2325251881Speter
2326251881Speter/* An svn_delta_editor_t function. */
2327251881Speterstatic svn_error_t *
2328251881Speterclose_file(void *file_baton,
2329251881Speter           const char *text_checksum,  /* ignored, as we receive no data */
2330251881Speter           apr_pool_t *pool)
2331251881Speter{
2332251881Speter  struct file_baton *fb = file_baton;
2333251881Speter  enum svn_wc_status_kind repos_node_status;
2334251881Speter  enum svn_wc_status_kind repos_text_status;
2335251881Speter  enum svn_wc_status_kind repos_prop_status;
2336251881Speter  const svn_lock_t *repos_lock = NULL;
2337251881Speter
2338251881Speter  /* If nothing has changed, return. */
2339251881Speter  if (! (fb->added || fb->prop_changed || fb->text_changed))
2340251881Speter    return SVN_NO_ERROR;
2341251881Speter
2342251881Speter  /* If this is a new file, add it to the statushash. */
2343251881Speter  if (fb->added)
2344251881Speter    {
2345251881Speter      repos_node_status = svn_wc_status_added;
2346251881Speter      repos_text_status = fb->text_changed ? svn_wc_status_modified
2347251881Speter                                           : 0 /* don't tweak */;
2348251881Speter      repos_prop_status = fb->prop_changed ? svn_wc_status_modified
2349251881Speter                                           : 0 /* don't tweak */;
2350251881Speter
2351251881Speter      if (fb->edit_baton->wb.repos_locks)
2352251881Speter        {
2353251881Speter          const char *dir_repos_relpath = find_dir_repos_relpath(fb->dir_baton,
2354251881Speter                                                                 pool);
2355251881Speter
2356251881Speter          /* repos_lock still uses the deprecated filesystem absolute path
2357251881Speter             format */
2358251881Speter          const char *repos_relpath = svn_relpath_join(dir_repos_relpath,
2359251881Speter                                                       fb->name, pool);
2360251881Speter
2361251881Speter          repos_lock = svn_hash_gets(fb->edit_baton->wb.repos_locks,
2362251881Speter                                     svn_fspath__join("/", repos_relpath,
2363251881Speter                                                      pool));
2364251881Speter        }
2365251881Speter    }
2366251881Speter  else
2367251881Speter    {
2368251881Speter      repos_node_status = (fb->text_changed || fb->prop_changed)
2369251881Speter                                 ? svn_wc_status_modified
2370251881Speter                                 : 0 /* don't tweak */;
2371251881Speter      repos_text_status = fb->text_changed ? svn_wc_status_modified
2372251881Speter                                           : 0 /* don't tweak */;
2373251881Speter      repos_prop_status = fb->prop_changed ? svn_wc_status_modified
2374251881Speter                                           : 0 /* don't tweak */;
2375251881Speter    }
2376251881Speter
2377251881Speter  return tweak_statushash(fb, NULL, FALSE, fb->edit_baton->db,
2378251881Speter                          fb->local_abspath, repos_node_status,
2379251881Speter                          repos_text_status, repos_prop_status,
2380251881Speter                          SVN_INVALID_REVNUM, repos_lock, pool);
2381251881Speter}
2382251881Speter
2383251881Speter/* An svn_delta_editor_t function. */
2384251881Speterstatic svn_error_t *
2385251881Speterclose_edit(void *edit_baton,
2386251881Speter           apr_pool_t *pool)
2387251881Speter{
2388251881Speter  struct edit_baton *eb = edit_baton;
2389251881Speter
2390251881Speter  /* If we get here and the root was not opened as part of the edit,
2391251881Speter     we need to transmit statuses for everything.  Otherwise, we
2392251881Speter     should be done. */
2393251881Speter  if (eb->root_opened)
2394251881Speter    return SVN_NO_ERROR;
2395251881Speter
2396251881Speter  SVN_ERR(svn_wc_walk_status(eb->wc_ctx,
2397251881Speter                             eb->target_abspath,
2398251881Speter                             eb->default_depth,
2399251881Speter                             eb->get_all,
2400251881Speter                             eb->no_ignore,
2401251881Speter                             FALSE,
2402251881Speter                             eb->ignores,
2403251881Speter                             eb->status_func,
2404251881Speter                             eb->status_baton,
2405251881Speter                             eb->cancel_func,
2406251881Speter                             eb->cancel_baton,
2407251881Speter                             pool));
2408251881Speter
2409251881Speter  return SVN_NO_ERROR;
2410251881Speter}
2411251881Speter
2412251881Speter
2413251881Speter
2414251881Speter/*** Public API ***/
2415251881Speter
2416251881Spetersvn_error_t *
2417251881Spetersvn_wc__get_status_editor(const svn_delta_editor_t **editor,
2418251881Speter                          void **edit_baton,
2419251881Speter                          void **set_locks_baton,
2420251881Speter                          svn_revnum_t *edit_revision,
2421251881Speter                          svn_wc_context_t *wc_ctx,
2422251881Speter                          const char *anchor_abspath,
2423251881Speter                          const char *target_basename,
2424251881Speter                          svn_depth_t depth,
2425251881Speter                          svn_boolean_t get_all,
2426251881Speter                          svn_boolean_t no_ignore,
2427251881Speter                          svn_boolean_t depth_as_sticky,
2428251881Speter                          svn_boolean_t server_performs_filtering,
2429251881Speter                          const apr_array_header_t *ignore_patterns,
2430251881Speter                          svn_wc_status_func4_t status_func,
2431251881Speter                          void *status_baton,
2432251881Speter                          svn_cancel_func_t cancel_func,
2433251881Speter                          void *cancel_baton,
2434251881Speter                          apr_pool_t *result_pool,
2435251881Speter                          apr_pool_t *scratch_pool)
2436251881Speter{
2437251881Speter  struct edit_baton *eb;
2438251881Speter  svn_delta_editor_t *tree_editor = svn_delta_default_editor(result_pool);
2439251881Speter  void *inner_baton;
2440251881Speter  struct svn_wc__shim_fetch_baton_t *sfb;
2441251881Speter  const svn_delta_editor_t *inner_editor;
2442251881Speter  svn_delta_shim_callbacks_t *shim_callbacks =
2443251881Speter                                svn_delta_shim_callbacks_default(result_pool);
2444251881Speter
2445251881Speter  /* Construct an edit baton. */
2446251881Speter  eb = apr_pcalloc(result_pool, sizeof(*eb));
2447251881Speter  eb->default_depth     = depth;
2448251881Speter  eb->target_revision   = edit_revision;
2449251881Speter  eb->db                = wc_ctx->db;
2450251881Speter  eb->wc_ctx            = wc_ctx;
2451251881Speter  eb->get_all           = get_all;
2452251881Speter  eb->no_ignore         = no_ignore;
2453251881Speter  eb->status_func       = status_func;
2454251881Speter  eb->status_baton      = status_baton;
2455251881Speter  eb->cancel_func       = cancel_func;
2456251881Speter  eb->cancel_baton      = cancel_baton;
2457251881Speter  eb->anchor_abspath    = apr_pstrdup(result_pool, anchor_abspath);
2458251881Speter  eb->target_abspath    = svn_dirent_join(anchor_abspath, target_basename,
2459251881Speter                                          result_pool);
2460251881Speter
2461251881Speter  eb->target_basename   = apr_pstrdup(result_pool, target_basename);
2462251881Speter  eb->root_opened       = FALSE;
2463251881Speter
2464251881Speter  eb->wb.db               = wc_ctx->db;
2465251881Speter  eb->wb.target_abspath   = eb->target_abspath;
2466251881Speter  eb->wb.ignore_text_mods = FALSE;
2467251881Speter  eb->wb.repos_locks      = NULL;
2468251881Speter  eb->wb.repos_root       = NULL;
2469251881Speter
2470251881Speter  SVN_ERR(svn_wc__db_externals_defined_below(&eb->wb.externals,
2471251881Speter                                             wc_ctx->db, eb->target_abspath,
2472251881Speter                                             result_pool, scratch_pool));
2473251881Speter
2474251881Speter  /* Use the caller-provided ignore patterns if provided; the build-time
2475251881Speter     configured defaults otherwise. */
2476251881Speter  if (ignore_patterns)
2477251881Speter    {
2478251881Speter      eb->ignores = ignore_patterns;
2479251881Speter    }
2480251881Speter  else
2481251881Speter    {
2482251881Speter      apr_array_header_t *ignores;
2483251881Speter
2484251881Speter      SVN_ERR(svn_wc_get_default_ignores(&ignores, NULL, result_pool));
2485251881Speter      eb->ignores = ignores;
2486251881Speter    }
2487251881Speter
2488251881Speter  /* The edit baton's status structure maps to PATH, and the editor
2489251881Speter     have to be aware of whether that is the anchor or the target. */
2490251881Speter  SVN_ERR(internal_status(&(eb->anchor_status), wc_ctx->db, anchor_abspath,
2491251881Speter                         result_pool, scratch_pool));
2492251881Speter
2493251881Speter  /* Construct an editor. */
2494251881Speter  tree_editor->set_target_revision = set_target_revision;
2495251881Speter  tree_editor->open_root = open_root;
2496251881Speter  tree_editor->delete_entry = delete_entry;
2497251881Speter  tree_editor->add_directory = add_directory;
2498251881Speter  tree_editor->open_directory = open_directory;
2499251881Speter  tree_editor->change_dir_prop = change_dir_prop;
2500251881Speter  tree_editor->close_directory = close_directory;
2501251881Speter  tree_editor->add_file = add_file;
2502251881Speter  tree_editor->open_file = open_file;
2503251881Speter  tree_editor->apply_textdelta = apply_textdelta;
2504251881Speter  tree_editor->change_file_prop = change_file_prop;
2505251881Speter  tree_editor->close_file = close_file;
2506251881Speter  tree_editor->close_edit = close_edit;
2507251881Speter
2508251881Speter  inner_editor = tree_editor;
2509251881Speter  inner_baton = eb;
2510251881Speter
2511251881Speter  if (!server_performs_filtering
2512251881Speter      && !depth_as_sticky)
2513251881Speter    SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
2514251881Speter                                                &inner_baton,
2515251881Speter                                                wc_ctx->db,
2516251881Speter                                                anchor_abspath,
2517251881Speter                                                target_basename,
2518251881Speter                                                inner_editor,
2519251881Speter                                                inner_baton,
2520251881Speter                                                result_pool));
2521251881Speter
2522251881Speter  /* Conjoin a cancellation editor with our status editor. */
2523251881Speter  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
2524251881Speter                                            inner_editor, inner_baton,
2525251881Speter                                            editor, edit_baton,
2526251881Speter                                            result_pool));
2527251881Speter
2528251881Speter  if (set_locks_baton)
2529251881Speter    *set_locks_baton = eb;
2530251881Speter
2531251881Speter  sfb = apr_palloc(result_pool, sizeof(*sfb));
2532251881Speter  sfb->db = wc_ctx->db;
2533251881Speter  sfb->base_abspath = eb->anchor_abspath;
2534251881Speter  sfb->fetch_base = FALSE;
2535251881Speter
2536251881Speter  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
2537251881Speter  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
2538251881Speter  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
2539251881Speter  shim_callbacks->fetch_baton = sfb;
2540251881Speter
2541251881Speter  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
2542251881Speter                                   NULL, NULL, shim_callbacks,
2543251881Speter                                   result_pool, scratch_pool));
2544251881Speter
2545251881Speter  return SVN_NO_ERROR;
2546251881Speter}
2547251881Speter
2548251881Speter/* Like svn_io_stat_dirent, but works case sensitive inside working
2549251881Speter   copies. Before 1.8 we handled this with a selection filter inside
2550251881Speter   a directory */
2551251881Speterstatic svn_error_t *
2552251881Speterstat_wc_dirent_case_sensitive(const svn_io_dirent2_t **dirent,
2553251881Speter                              svn_wc__db_t *db,
2554251881Speter                              const char *local_abspath,
2555251881Speter                              apr_pool_t *result_pool,
2556251881Speter                              apr_pool_t *scratch_pool)
2557251881Speter{
2558251881Speter  svn_boolean_t is_wcroot;
2559251881Speter
2560251881Speter  /* The wcroot is "" inside the wc; handle it as not in the wc, as
2561251881Speter     the case of the root is indifferent to us. */
2562251881Speter
2563251881Speter  /* Note that for performance this is really just a few hashtable lookups,
2564251881Speter     as we just used local_abspath for a db call in both our callers */
2565251881Speter  SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath,
2566251881Speter                               scratch_pool));
2567251881Speter
2568251881Speter  return svn_error_trace(
2569251881Speter            svn_io_stat_dirent2(dirent, local_abspath,
2570251881Speter                                ! is_wcroot /* verify_truename */,
2571251881Speter                                TRUE        /* ignore_enoent */,
2572251881Speter                                result_pool, scratch_pool));
2573251881Speter}
2574251881Speter
2575251881Spetersvn_error_t *
2576251881Spetersvn_wc__internal_walk_status(svn_wc__db_t *db,
2577251881Speter                             const char *local_abspath,
2578251881Speter                             svn_depth_t depth,
2579251881Speter                             svn_boolean_t get_all,
2580251881Speter                             svn_boolean_t no_ignore,
2581251881Speter                             svn_boolean_t ignore_text_mods,
2582251881Speter                             const apr_array_header_t *ignore_patterns,
2583251881Speter                             svn_wc_status_func4_t status_func,
2584251881Speter                             void *status_baton,
2585251881Speter                             svn_cancel_func_t cancel_func,
2586251881Speter                             void *cancel_baton,
2587251881Speter                             apr_pool_t *scratch_pool)
2588251881Speter{
2589251881Speter  struct walk_status_baton wb;
2590251881Speter  const svn_io_dirent2_t *dirent;
2591251881Speter  const struct svn_wc__db_info_t *info;
2592251881Speter  svn_error_t *err;
2593251881Speter
2594251881Speter  wb.db = db;
2595251881Speter  wb.target_abspath = local_abspath;
2596251881Speter  wb.ignore_text_mods = ignore_text_mods;
2597251881Speter  wb.repos_root = NULL;
2598251881Speter  wb.repos_locks = NULL;
2599251881Speter
2600251881Speter  /* Use the caller-provided ignore patterns if provided; the build-time
2601251881Speter     configured defaults otherwise. */
2602251881Speter  if (!ignore_patterns)
2603251881Speter    {
2604251881Speter      apr_array_header_t *ignores;
2605251881Speter
2606251881Speter      SVN_ERR(svn_wc_get_default_ignores(&ignores, NULL, scratch_pool));
2607251881Speter      ignore_patterns = ignores;
2608251881Speter    }
2609251881Speter
2610269847Speter  err = svn_wc__db_read_single_info(&info, db, local_abspath,
2611269847Speter                                    scratch_pool, scratch_pool);
2612251881Speter
2613251881Speter  if (err)
2614251881Speter    {
2615251881Speter      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
2616251881Speter        {
2617251881Speter          svn_error_clear(err);
2618251881Speter          info = NULL;
2619251881Speter        }
2620251881Speter      else
2621251881Speter        return svn_error_trace(err);
2622251881Speter
2623251881Speter      wb.externals = apr_hash_make(scratch_pool);
2624251881Speter
2625251881Speter      SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE,
2626251881Speter                                  scratch_pool, scratch_pool));
2627251881Speter    }
2628251881Speter  else
2629251881Speter    {
2630251881Speter      SVN_ERR(svn_wc__db_externals_defined_below(&wb.externals,
2631251881Speter                                                 db, local_abspath,
2632251881Speter                                                 scratch_pool, scratch_pool));
2633251881Speter
2634251881Speter      SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath,
2635251881Speter                                            scratch_pool, scratch_pool));
2636251881Speter    }
2637251881Speter
2638251881Speter  if (info
2639251881Speter      && info->kind == svn_node_dir
2640251881Speter      && info->status != svn_wc__db_status_not_present
2641251881Speter      && info->status != svn_wc__db_status_excluded
2642251881Speter      && info->status != svn_wc__db_status_server_excluded)
2643251881Speter    {
2644251881Speter      SVN_ERR(get_dir_status(&wb,
2645251881Speter                             local_abspath,
2646251881Speter                             FALSE /* skip_root */,
2647251881Speter                             NULL, NULL, NULL,
2648251881Speter                             info,
2649251881Speter                             dirent,
2650251881Speter                             ignore_patterns,
2651251881Speter                             depth,
2652251881Speter                             get_all,
2653251881Speter                             no_ignore,
2654251881Speter                             status_func, status_baton,
2655251881Speter                             cancel_func, cancel_baton,
2656251881Speter                             scratch_pool));
2657251881Speter    }
2658251881Speter  else
2659251881Speter    {
2660251881Speter      /* It may be a file or an unversioned item. And this is an explicit
2661251881Speter       * target, so no ignoring. An unversioned item (file or dir) shows a
2662251881Speter       * status like '?', and can yield a tree conflicted path. */
2663251881Speter      err = get_child_status(&wb,
2664251881Speter                             local_abspath,
2665251881Speter                             info,
2666251881Speter                             dirent,
2667251881Speter                             ignore_patterns,
2668251881Speter                             get_all,
2669251881Speter                             status_func, status_baton,
2670251881Speter                             cancel_func, cancel_baton,
2671251881Speter                             scratch_pool);
2672251881Speter
2673251881Speter      if (!info && err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
2674251881Speter        {
2675251881Speter          /* The parent is also not versioned, but it is not nice to show
2676251881Speter             an error about a path a user didn't intend to touch. */
2677251881Speter          svn_error_clear(err);
2678251881Speter          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2679251881Speter                                   _("The node '%s' was not found."),
2680251881Speter                                   svn_dirent_local_style(local_abspath,
2681251881Speter                                                          scratch_pool));
2682251881Speter        }
2683251881Speter      SVN_ERR(err);
2684251881Speter    }
2685251881Speter
2686251881Speter  return SVN_NO_ERROR;
2687251881Speter}
2688251881Speter
2689251881Spetersvn_error_t *
2690251881Spetersvn_wc_walk_status(svn_wc_context_t *wc_ctx,
2691251881Speter                   const char *local_abspath,
2692251881Speter                   svn_depth_t depth,
2693251881Speter                   svn_boolean_t get_all,
2694251881Speter                   svn_boolean_t no_ignore,
2695251881Speter                   svn_boolean_t ignore_text_mods,
2696251881Speter                   const apr_array_header_t *ignore_patterns,
2697251881Speter                   svn_wc_status_func4_t status_func,
2698251881Speter                   void *status_baton,
2699251881Speter                   svn_cancel_func_t cancel_func,
2700251881Speter                   void *cancel_baton,
2701251881Speter                   apr_pool_t *scratch_pool)
2702251881Speter{
2703251881Speter  return svn_error_trace(
2704251881Speter           svn_wc__internal_walk_status(wc_ctx->db,
2705251881Speter                                        local_abspath,
2706251881Speter                                        depth,
2707251881Speter                                        get_all,
2708251881Speter                                        no_ignore,
2709251881Speter                                        ignore_text_mods,
2710251881Speter                                        ignore_patterns,
2711251881Speter                                        status_func,
2712251881Speter                                        status_baton,
2713251881Speter                                        cancel_func,
2714251881Speter                                        cancel_baton,
2715251881Speter                                        scratch_pool));
2716251881Speter}
2717251881Speter
2718251881Speter
2719251881Spetersvn_error_t *
2720251881Spetersvn_wc_status_set_repos_locks(void *edit_baton,
2721251881Speter                              apr_hash_t *locks,
2722251881Speter                              const char *repos_root,
2723251881Speter                              apr_pool_t *pool)
2724251881Speter{
2725251881Speter  struct edit_baton *eb = edit_baton;
2726251881Speter
2727251881Speter  eb->wb.repos_locks = locks;
2728251881Speter  eb->wb.repos_root = apr_pstrdup(pool, repos_root);
2729251881Speter
2730251881Speter  return SVN_NO_ERROR;
2731251881Speter}
2732251881Speter
2733251881Speter
2734251881Spetersvn_error_t *
2735251881Spetersvn_wc_get_default_ignores(apr_array_header_t **patterns,
2736251881Speter                           apr_hash_t *config,
2737251881Speter                           apr_pool_t *pool)
2738251881Speter{
2739251881Speter  svn_config_t *cfg = config
2740251881Speter                      ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG)
2741251881Speter                      : NULL;
2742251881Speter  const char *val;
2743251881Speter
2744251881Speter  /* Check the Subversion run-time configuration for global ignores.
2745251881Speter     If no configuration value exists, we fall back to our defaults. */
2746251881Speter  svn_config_get(cfg, &val, SVN_CONFIG_SECTION_MISCELLANY,
2747251881Speter                 SVN_CONFIG_OPTION_GLOBAL_IGNORES,
2748251881Speter                 SVN_CONFIG_DEFAULT_GLOBAL_IGNORES);
2749251881Speter  *patterns = apr_array_make(pool, 16, sizeof(const char *));
2750251881Speter
2751251881Speter  /* Split the patterns on whitespace, and stuff them into *PATTERNS. */
2752251881Speter  svn_cstring_split_append(*patterns, val, "\n\r\t\v ", FALSE, pool);
2753251881Speter  return SVN_NO_ERROR;
2754251881Speter}
2755251881Speter
2756251881Speter
2757251881Speter/* */
2758251881Speterstatic svn_error_t *
2759251881Speterinternal_status(svn_wc_status3_t **status,
2760251881Speter                svn_wc__db_t *db,
2761251881Speter                const char *local_abspath,
2762251881Speter                apr_pool_t *result_pool,
2763251881Speter                apr_pool_t *scratch_pool)
2764251881Speter{
2765251881Speter  const svn_io_dirent2_t *dirent;
2766251881Speter  svn_node_kind_t node_kind;
2767251881Speter  const char *parent_repos_relpath;
2768251881Speter  const char *parent_repos_root_url;
2769251881Speter  const char *parent_repos_uuid;
2770251881Speter  svn_wc__db_status_t node_status;
2771251881Speter  svn_boolean_t conflicted;
2772251881Speter  svn_boolean_t is_root = FALSE;
2773251881Speter  svn_error_t *err;
2774251881Speter
2775251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2776251881Speter
2777251881Speter  err = svn_wc__db_read_info(&node_status, &node_kind, NULL, NULL, NULL, NULL,
2778251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2779251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
2780251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL,
2781251881Speter                             db, local_abspath,
2782251881Speter                             scratch_pool, scratch_pool);
2783251881Speter
2784251881Speter  if (err)
2785251881Speter    {
2786251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2787251881Speter        return svn_error_trace(err);
2788251881Speter
2789251881Speter      svn_error_clear(err);
2790251881Speter      node_kind = svn_node_unknown;
2791251881Speter      /* Ensure conflicted is always set, but don't hide tree conflicts
2792251881Speter         on 'hidden' nodes. */
2793251881Speter      conflicted = FALSE;
2794251881Speter
2795251881Speter      SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE,
2796251881Speter                                  scratch_pool, scratch_pool));
2797251881Speter    }
2798251881Speter  else
2799251881Speter    SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath,
2800251881Speter                                          scratch_pool, scratch_pool));
2801251881Speter
2802251881Speter  if (node_kind != svn_node_unknown
2803251881Speter      && (node_status == svn_wc__db_status_not_present
2804251881Speter          || node_status == svn_wc__db_status_server_excluded
2805251881Speter          || node_status == svn_wc__db_status_excluded))
2806251881Speter    {
2807251881Speter      node_kind = svn_node_unknown;
2808251881Speter    }
2809251881Speter
2810251881Speter  if (node_kind == svn_node_unknown)
2811251881Speter    return svn_error_trace(assemble_unversioned(status,
2812251881Speter                                                db, local_abspath,
2813251881Speter                                                dirent, conflicted,
2814251881Speter                                                FALSE /* is_ignored */,
2815251881Speter                                                result_pool, scratch_pool));
2816251881Speter
2817251881Speter  if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
2818251881Speter    is_root = TRUE;
2819251881Speter  else
2820251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
2821251881Speter
2822251881Speter  if (!is_root)
2823251881Speter    {
2824251881Speter      svn_wc__db_status_t parent_status;
2825251881Speter      const char *parent_abspath = svn_dirent_dirname(local_abspath,
2826251881Speter                                                      scratch_pool);
2827251881Speter
2828251881Speter      err = svn_wc__db_read_info(&parent_status, NULL, NULL,
2829251881Speter                                 &parent_repos_relpath, &parent_repos_root_url,
2830251881Speter                                 &parent_repos_uuid, NULL, NULL, NULL,
2831251881Speter                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2832251881Speter                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2833251881Speter                                 NULL, NULL, NULL, NULL,
2834251881Speter                                 db, parent_abspath,
2835251881Speter                                 result_pool, scratch_pool);
2836251881Speter
2837251881Speter      if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
2838251881Speter                  || SVN_WC__ERR_IS_NOT_CURRENT_WC(err)))
2839251881Speter        {
2840251881Speter          svn_error_clear(err);
2841251881Speter          parent_repos_root_url = NULL;
2842251881Speter          parent_repos_relpath = NULL;
2843251881Speter          parent_repos_uuid = NULL;
2844251881Speter        }
2845251881Speter      else SVN_ERR(err);
2846251881Speter    }
2847251881Speter  else
2848251881Speter    {
2849251881Speter      parent_repos_root_url = NULL;
2850251881Speter      parent_repos_relpath = NULL;
2851251881Speter      parent_repos_uuid = NULL;
2852251881Speter    }
2853251881Speter
2854251881Speter  return svn_error_trace(assemble_status(status, db, local_abspath,
2855251881Speter                                         parent_repos_root_url,
2856251881Speter                                         parent_repos_relpath,
2857251881Speter                                         parent_repos_uuid,
2858251881Speter                                         NULL,
2859251881Speter                                         dirent,
2860251881Speter                                         TRUE /* get_all */,
2861251881Speter                                         FALSE,
2862251881Speter                                         NULL /* repos_lock */,
2863251881Speter                                         result_pool, scratch_pool));
2864251881Speter}
2865251881Speter
2866251881Speter
2867251881Spetersvn_error_t *
2868251881Spetersvn_wc_status3(svn_wc_status3_t **status,
2869251881Speter               svn_wc_context_t *wc_ctx,
2870251881Speter               const char *local_abspath,
2871251881Speter               apr_pool_t *result_pool,
2872251881Speter               apr_pool_t *scratch_pool)
2873251881Speter{
2874251881Speter  return svn_error_trace(
2875251881Speter    internal_status(status, wc_ctx->db, local_abspath, result_pool,
2876251881Speter                    scratch_pool));
2877251881Speter}
2878251881Speter
2879251881Spetersvn_wc_status3_t *
2880251881Spetersvn_wc_dup_status3(const svn_wc_status3_t *orig_stat,
2881251881Speter                   apr_pool_t *pool)
2882251881Speter{
2883251881Speter  svn_wc_status3_t *new_stat = apr_palloc(pool, sizeof(*new_stat));
2884251881Speter
2885251881Speter  /* Shallow copy all members. */
2886251881Speter  *new_stat = *orig_stat;
2887251881Speter
2888251881Speter  /* Now go back and dup the deep items into this pool. */
2889251881Speter  if (orig_stat->repos_lock)
2890251881Speter    new_stat->repos_lock = svn_lock_dup(orig_stat->repos_lock, pool);
2891251881Speter
2892251881Speter  if (orig_stat->changed_author)
2893251881Speter    new_stat->changed_author = apr_pstrdup(pool, orig_stat->changed_author);
2894251881Speter
2895251881Speter  if (orig_stat->ood_changed_author)
2896251881Speter    new_stat->ood_changed_author
2897251881Speter      = apr_pstrdup(pool, orig_stat->ood_changed_author);
2898251881Speter
2899251881Speter  if (orig_stat->lock)
2900251881Speter    new_stat->lock = svn_lock_dup(orig_stat->lock, pool);
2901251881Speter
2902251881Speter  if (orig_stat->changelist)
2903251881Speter    new_stat->changelist
2904251881Speter      = apr_pstrdup(pool, orig_stat->changelist);
2905251881Speter
2906251881Speter  if (orig_stat->repos_root_url)
2907251881Speter    new_stat->repos_root_url
2908251881Speter      = apr_pstrdup(pool, orig_stat->repos_root_url);
2909251881Speter
2910251881Speter  if (orig_stat->repos_relpath)
2911251881Speter    new_stat->repos_relpath
2912251881Speter      = apr_pstrdup(pool, orig_stat->repos_relpath);
2913251881Speter
2914251881Speter  if (orig_stat->repos_uuid)
2915251881Speter    new_stat->repos_uuid
2916251881Speter      = apr_pstrdup(pool, orig_stat->repos_uuid);
2917251881Speter
2918251881Speter  if (orig_stat->moved_from_abspath)
2919251881Speter    new_stat->moved_from_abspath
2920251881Speter      = apr_pstrdup(pool, orig_stat->moved_from_abspath);
2921251881Speter
2922251881Speter  if (orig_stat->moved_to_abspath)
2923251881Speter    new_stat->moved_to_abspath
2924251881Speter      = apr_pstrdup(pool, orig_stat->moved_to_abspath);
2925251881Speter
2926251881Speter  /* Return the new hotness. */
2927251881Speter  return new_stat;
2928251881Speter}
2929251881Speter
2930251881Spetersvn_error_t *
2931251881Spetersvn_wc_get_ignores2(apr_array_header_t **patterns,
2932251881Speter                    svn_wc_context_t *wc_ctx,
2933251881Speter                    const char *local_abspath,
2934251881Speter                    apr_hash_t *config,
2935251881Speter                    apr_pool_t *result_pool,
2936251881Speter                    apr_pool_t *scratch_pool)
2937251881Speter{
2938251881Speter  apr_array_header_t *default_ignores;
2939251881Speter
2940251881Speter  SVN_ERR(svn_wc_get_default_ignores(&default_ignores, config, scratch_pool));
2941251881Speter  return svn_error_trace(collect_ignore_patterns(patterns, wc_ctx->db,
2942251881Speter                                                 local_abspath,
2943251881Speter                                                 default_ignores,
2944251881Speter                                                 result_pool, scratch_pool));
2945251881Speter}
2946