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