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