add.c revision 251886
1/*
2 * add.c:  wrappers around wc add/mkdir functionality.
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24/* ==================================================================== */
25
26
27
28/*** Includes. ***/
29
30#include <string.h>
31#include <apr_lib.h>
32#include <apr_fnmatch.h>
33#include "svn_wc.h"
34#include "svn_client.h"
35#include "svn_string.h"
36#include "svn_pools.h"
37#include "svn_error.h"
38#include "svn_dirent_uri.h"
39#include "svn_path.h"
40#include "svn_io.h"
41#include "svn_config.h"
42#include "svn_props.h"
43#include "svn_hash.h"
44#include "svn_sorts.h"
45#include "client.h"
46#include "svn_ctype.h"
47
48#include "private/svn_client_private.h"
49#include "private/svn_wc_private.h"
50#include "private/svn_ra_private.h"
51#include "private/svn_magic.h"
52
53#include "svn_private_config.h"
54
55
56
57/*** Code. ***/
58
59/* Remove leading and trailing white space from a C string, in place. */
60static void
61trim_string(char **pstr)
62{
63  char *str = *pstr;
64  size_t i;
65
66  while (svn_ctype_isspace(*str))
67    str++;
68  *pstr = str;
69  i = strlen(str);
70  while ((i > 0) && svn_ctype_isspace(str[i-1]))
71    i--;
72  str[i] = '\0';
73}
74
75/* Remove leading and trailing single- or double quotes from a C string,
76 * in place. */
77static void
78unquote_string(char **pstr)
79{
80  char *str = *pstr;
81  size_t i = strlen(str);
82
83  if (i > 0 && ((*str == '"' && str[i - 1] == '"') ||
84                (*str == '\'' && str[i - 1] == '\'')))
85    {
86      str[i - 1] = '\0';
87      str++;
88    }
89  *pstr = str;
90}
91
92/* Split PROPERTY and store each individual value in PROPS.
93   Allocates from POOL. */
94static void
95split_props(apr_array_header_t **props,
96            const char *property,
97            apr_pool_t *pool)
98{
99  apr_array_header_t *temp_props;
100  char *new_prop;
101  int i = 0;
102  int j = 0;
103
104  temp_props = apr_array_make(pool, 4, sizeof(char *));
105  new_prop = apr_palloc(pool, strlen(property)+1);
106
107  for (i = 0; property[i] != '\0'; i++)
108    {
109      if (property[i] != ';')
110        {
111          new_prop[j] = property[i];
112          j++;
113        }
114      else if (property[i] == ';')
115        {
116          /* ";;" becomes ";" */
117          if (property[i+1] == ';')
118            {
119              new_prop[j] = ';';
120              j++;
121              i++;
122            }
123          else
124            {
125              new_prop[j] = '\0';
126              APR_ARRAY_PUSH(temp_props, char *) = new_prop;
127              new_prop += j + 1;
128              j = 0;
129            }
130        }
131    }
132  new_prop[j] = '\0';
133  APR_ARRAY_PUSH(temp_props, char *) = new_prop;
134  *props = temp_props;
135}
136
137/* PROPVALS is a hash mapping char * property names to const char * property
138   values.  PROPERTIES can be empty but not NULL.
139
140   If FILENAME doesn't match the filename pattern PATTERN case insensitively,
141   the do nothing.  Otherwise for each 'name':'value' pair in PROPVALS, add
142   a new entry mappying 'name' to a svn_string_t * wrapping the 'value' in
143   PROPERTIES.  The svn_string_t is allocated in the pool used to allocate
144   PROPERTIES, but the char *'s from PROPVALS are re-used in PROPERTIES.
145   If PROPVALS contains a 'svn:mime-type' mapping, then set *MIMETYPE to
146   the mapped value.  Likewise if PROPVALS contains a mapping for
147   svn:executable, then set *HAVE_EXECUTABLE to TRUE.
148
149   Use SCRATCH_POOL for temporary allocations.
150*/
151static void
152get_auto_props_for_pattern(apr_hash_t *properties,
153                           const char **mimetype,
154                           svn_boolean_t *have_executable,
155                           const char *filename,
156                           const char *pattern,
157                           apr_hash_t *propvals,
158                           apr_pool_t *scratch_pool)
159{
160  apr_hash_index_t *hi;
161
162  /* check if filename matches and return if it doesn't */
163  if (apr_fnmatch(pattern, filename,
164                  APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH)
165    return;
166
167  for (hi = apr_hash_first(scratch_pool, propvals);
168       hi != NULL;
169       hi = apr_hash_next(hi))
170    {
171      const char *propname = svn__apr_hash_index_key(hi);
172      const char *propval = svn__apr_hash_index_val(hi);
173      svn_string_t *propval_str =
174        svn_string_create_empty(apr_hash_pool_get(properties));
175
176      propval_str->data = propval;
177      propval_str->len = strlen(propval);
178
179      svn_hash_sets(properties, propname, propval_str);
180      if (strcmp(propname, SVN_PROP_MIME_TYPE) == 0)
181        *mimetype = propval;
182      else if (strcmp(propname, SVN_PROP_EXECUTABLE) == 0)
183        *have_executable = TRUE;
184    }
185}
186
187svn_error_t *
188svn_client__get_paths_auto_props(apr_hash_t **properties,
189                                 const char **mimetype,
190                                 const char *path,
191                                 svn_magic__cookie_t *magic_cookie,
192                                 apr_hash_t *autoprops,
193                                 svn_client_ctx_t *ctx,
194                                 apr_pool_t *result_pool,
195                                 apr_pool_t *scratch_pool)
196{
197  apr_hash_index_t *hi;
198  svn_boolean_t have_executable = FALSE;
199
200  *properties = apr_hash_make(result_pool);
201  *mimetype = NULL;
202
203  if (autoprops)
204    {
205      for (hi = apr_hash_first(scratch_pool, autoprops);
206           hi != NULL;
207           hi = apr_hash_next(hi))
208        {
209          const char *pattern = svn__apr_hash_index_key(hi);
210          apr_hash_t *propvals = svn__apr_hash_index_val(hi);
211
212          get_auto_props_for_pattern(*properties, mimetype, &have_executable,
213                                     svn_dirent_basename(path, scratch_pool),
214                                     pattern, propvals, scratch_pool);
215        }
216    }
217
218  /* if mimetype has not been set check the file */
219  if (! *mimetype)
220    {
221      SVN_ERR(svn_io_detect_mimetype2(mimetype, path, ctx->mimetypes_map,
222                                      result_pool));
223
224      /* If we got no mime-type, or if it is "application/octet-stream",
225       * try to get the mime-type from libmagic. */
226      if (magic_cookie &&
227          (!*mimetype ||
228           strcmp(*mimetype, "application/octet-stream") == 0))
229        {
230          const char *magic_mimetype;
231
232         /* Since libmagic usually treats UTF-16 files as "text/plain",
233          * svn_magic__detect_binary_mimetype() will return NULL for such
234          * files. This is fine for now since we currently don't support
235          * UTF-16-encoded text files (issue #2194).
236          * Once we do support UTF-16 this code path will fail to detect
237          * them as text unless the svn_io_detect_mimetype2() call above
238          * returns "text/plain" for them. */
239          SVN_ERR(svn_magic__detect_binary_mimetype(&magic_mimetype,
240                                                    path, magic_cookie,
241                                                    result_pool,
242                                                    scratch_pool));
243          if (magic_mimetype)
244            *mimetype = magic_mimetype;
245        }
246
247      if (*mimetype)
248        apr_hash_set(*properties, SVN_PROP_MIME_TYPE,
249                     strlen(SVN_PROP_MIME_TYPE),
250                     svn_string_create(*mimetype, result_pool));
251    }
252
253  /* if executable has not been set check the file */
254  if (! have_executable)
255    {
256      svn_boolean_t executable = FALSE;
257      SVN_ERR(svn_io_is_file_executable(&executable, path, scratch_pool));
258      if (executable)
259        apr_hash_set(*properties, SVN_PROP_EXECUTABLE,
260                     strlen(SVN_PROP_EXECUTABLE),
261                     svn_string_create_empty(result_pool));
262    }
263
264  return SVN_NO_ERROR;
265}
266
267/* Only call this if the on-disk node kind is a file. */
268static svn_error_t *
269add_file(const char *local_abspath,
270         svn_magic__cookie_t *magic_cookie,
271         apr_hash_t *autoprops,
272         svn_boolean_t no_autoprops,
273         svn_client_ctx_t *ctx,
274         apr_pool_t *pool)
275{
276  apr_hash_t *properties;
277  const char *mimetype;
278  svn_node_kind_t kind;
279  svn_boolean_t is_special;
280
281  /* Check to see if this is a special file. */
282  SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, pool));
283
284  /* Determine the properties that the file should have */
285  if (is_special)
286    {
287      mimetype = NULL;
288      properties = apr_hash_make(pool);
289      svn_hash_sets(properties, SVN_PROP_SPECIAL,
290                    svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
291    }
292  else
293    {
294      apr_hash_t *file_autoprops = NULL;
295
296      /* Get automatic properties */
297      /* If we are setting autoprops grab the inherited svn:auto-props and
298         config file auto-props for this file if we haven't already got them
299         when iterating over the file's unversioned parents. */
300      if (!no_autoprops)
301        {
302          if (autoprops == NULL)
303            SVN_ERR(svn_client__get_all_auto_props(
304              &file_autoprops, svn_dirent_dirname(local_abspath,pool),
305              ctx, pool, pool));
306          else
307            file_autoprops = autoprops;
308        }
309
310      /* This may fail on write-only files:
311         we open them to estimate file type. */
312      SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype,
313                                               local_abspath, magic_cookie,
314                                               file_autoprops, ctx, pool,
315                                               pool));
316    }
317
318  /* Add the file */
319  SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath, properties,
320                                ctx->notify_func2, ctx->notify_baton2, pool));
321
322  return SVN_NO_ERROR;
323}
324
325/* Schedule directory DIR_ABSPATH, and some of the tree under it, for
326 * addition.  DEPTH is the depth at this point in the descent (it may
327 * be changed for recursive calls).
328 *
329 * If DIR_ABSPATH (or any item below DIR_ABSPATH) is already scheduled for
330 * addition, add will fail and return an error unless FORCE is TRUE.
331 *
332 * Use MAGIC_COOKIE (which may be NULL) to detect the mime-type of files
333 * if necessary.
334 *
335 * If not NULL, CONFIG_AUTOPROPS is a hash representing the config file and
336 * svn:auto-props autoprops which apply to DIR_ABSPATH.  It maps
337 * const char * file patterns to another hash which maps const char *
338 * property names to const char *property values.  If CONFIG_AUTOPROPS is
339 * NULL and the config file and svn:auto-props autoprops are required by this
340 * function, then such will be obtained.
341 *
342 * If IGNORES is not NULL, then it is an array of const char * ignore patterns
343 * that apply to any children of DIR_ABSPATH.  If REFRESH_IGNORES is TRUE, then
344 * the passed in value of IGNORES (if any) is itself ignored and this function
345 * will gather all ignore patterns applicable to DIR_ABSPATH itself (allocated in
346 * RESULT_POOL).  Any recursive calls to this function get the refreshed ignore
347 * patterns.  If IGNORES is NULL and REFRESH_IGNORES is FALSE, then all children of DIR_ABSPATH
348 * are unconditionally added.
349 *
350 * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow
351 * the user to cancel the operation.
352 *
353 * Use SCRATCH_POOL for temporary allocations.
354 */
355static svn_error_t *
356add_dir_recursive(const char *dir_abspath,
357                  svn_depth_t depth,
358                  svn_boolean_t force,
359                  svn_boolean_t no_autoprops,
360                  svn_magic__cookie_t *magic_cookie,
361                  apr_hash_t *config_autoprops,
362                  svn_boolean_t refresh_ignores,
363                  apr_array_header_t *ignores,
364                  svn_client_ctx_t *ctx,
365                  apr_pool_t *result_pool,
366                  apr_pool_t *scratch_pool)
367{
368  svn_error_t *err;
369  apr_pool_t *iterpool;
370  apr_hash_t *dirents;
371  apr_hash_index_t *hi;
372  svn_boolean_t entry_exists = FALSE;
373
374  /* Check cancellation; note that this catches recursive calls too. */
375  if (ctx->cancel_func)
376    SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
377
378  iterpool = svn_pool_create(scratch_pool);
379
380  /* Add this directory to revision control. */
381  err = svn_wc_add_from_disk2(ctx->wc_ctx, dir_abspath, NULL /*props*/,
382                              ctx->notify_func2, ctx->notify_baton2,
383                              iterpool);
384  if (err)
385    {
386      if (err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
387        {
388          svn_error_clear(err);
389          entry_exists = TRUE;
390        }
391      else if (err)
392        {
393          return svn_error_trace(err);
394        }
395    }
396
397  /* Fetch ignores after adding to handle ignores on the directory itself
398     and ancestors via the single db optimization in libsvn_wc */
399  if (refresh_ignores)
400    SVN_ERR(svn_wc_get_ignores2(&ignores, ctx->wc_ctx, dir_abspath,
401                                ctx->config, result_pool, iterpool));
402
403  /* If DIR_ABSPATH is the root of an unversioned subtree then get the
404     following "autoprops":
405
406       1) Explicit and inherited svn:auto-props properties on
407          DIR_ABSPATH
408       2) auto-props from the CTX->CONFIG hash
409
410     Since this set of autoprops applies to all unversioned children of
411     DIR_ABSPATH, we will pass these along to any recursive calls to
412     add_dir_recursive() and calls to add_file() below.  Thus sparing
413     these callees from looking up the same information. */
414  if (!entry_exists && config_autoprops == NULL)
415    {
416      SVN_ERR(svn_client__get_all_auto_props(&config_autoprops, dir_abspath,
417                                             ctx, scratch_pool, iterpool));
418    }
419
420  SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, scratch_pool,
421                              iterpool));
422
423  /* Read the directory entries one by one and add those things to
424     version control. */
425  for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
426    {
427      const char *name = svn__apr_hash_index_key(hi);
428      svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
429      const char *abspath;
430
431      svn_pool_clear(iterpool);
432
433      /* Check cancellation so you can cancel during an
434       * add of a directory with lots of files. */
435      if (ctx->cancel_func)
436        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
437
438      /* Skip over SVN admin directories. */
439      if (svn_wc_is_adm_dir(name, iterpool))
440        continue;
441
442      if (ignores
443          && svn_wc_match_ignore_list(name, ignores, iterpool))
444        continue;
445
446      /* Construct the full path of the entry. */
447      abspath = svn_dirent_join(dir_abspath, name, iterpool);
448
449      /* Recurse on directories; add files; ignore the rest. */
450      if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
451        {
452          svn_depth_t depth_below_here = depth;
453          if (depth == svn_depth_immediates)
454            depth_below_here = svn_depth_empty;
455
456          /* When DIR_ABSPATH is the root of an unversioned subtree then
457             it and all of its children have the same set of ignores.  So
458             save any recursive calls the extra work of finding the same
459             set of ignores. */
460          if (refresh_ignores && !entry_exists)
461            refresh_ignores = FALSE;
462
463          SVN_ERR(add_dir_recursive(abspath, depth_below_here,
464                                    force, no_autoprops,
465                                    magic_cookie, config_autoprops,
466                                    refresh_ignores, ignores, ctx,
467                                    result_pool, iterpool));
468        }
469      else if ((dirent->kind == svn_node_file || dirent->special)
470               && depth >= svn_depth_files)
471        {
472          err = add_file(abspath, magic_cookie, config_autoprops,
473                         no_autoprops, ctx, iterpool);
474          if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
475            svn_error_clear(err);
476          else
477            SVN_ERR(err);
478        }
479    }
480
481  /* Destroy the per-iteration pool. */
482  svn_pool_destroy(iterpool);
483
484  return SVN_NO_ERROR;
485}
486
487/* This structure is used as baton for collecting the config entries
488   in the auto-props section and any inherited svn:auto-props
489   properties.
490*/
491typedef struct collect_auto_props_baton_t
492{
493  /* the hash table for storing the property name/value pairs */
494  apr_hash_t *autoprops;
495
496  /* a pool used for allocating memory */
497  apr_pool_t *result_pool;
498} collect_auto_props_baton_t;
499
500/* Implements svn_config_enumerator2_t callback.
501
502   For one auto-props config entry (NAME, VALUE), stash a copy of
503   NAME and VALUE, allocated in BATON->POOL, in BATON->AUTOPROP.
504   BATON must point to an collect_auto_props_baton_t.
505*/
506static svn_boolean_t
507all_auto_props_collector(const char *name,
508                         const char *value,
509                         void *baton,
510                         apr_pool_t *pool)
511{
512  collect_auto_props_baton_t *autoprops_baton = baton;
513  apr_array_header_t *autoprops;
514  int i;
515
516  /* nothing to do here without a value */
517  if (*value == 0)
518    return TRUE;
519
520  split_props(&autoprops, value, pool);
521
522  for (i = 0; i < autoprops->nelts; i ++)
523    {
524      size_t len;
525      const char *this_value;
526      char *property = APR_ARRAY_IDX(autoprops, i, char *);
527      char *equal_sign = strchr(property, '=');
528
529      if (equal_sign)
530        {
531          *equal_sign = '\0';
532          equal_sign++;
533          trim_string(&equal_sign);
534          unquote_string(&equal_sign);
535          this_value = equal_sign;
536        }
537      else
538        {
539          this_value = "";
540        }
541      trim_string(&property);
542      len = strlen(property);
543
544      if (len > 0)
545        {
546          apr_hash_t *pattern_hash = svn_hash_gets(autoprops_baton->autoprops,
547                                                   name);
548          svn_string_t *propval;
549
550          /* Force reserved boolean property values to '*'. */
551          if (svn_prop_is_boolean(property))
552            {
553              /* SVN_PROP_EXECUTABLE, SVN_PROP_NEEDS_LOCK, SVN_PROP_SPECIAL */
554              propval = svn_string_create("*", autoprops_baton->result_pool);
555            }
556          else
557            {
558              propval = svn_string_create(this_value,
559                                          autoprops_baton->result_pool);
560            }
561
562          if (!pattern_hash)
563            {
564              pattern_hash = apr_hash_make(autoprops_baton->result_pool);
565              svn_hash_sets(autoprops_baton->autoprops,
566                            apr_pstrdup(autoprops_baton->result_pool, name),
567                            pattern_hash);
568            }
569          svn_hash_sets(pattern_hash,
570                        apr_pstrdup(autoprops_baton->result_pool, property),
571                        propval->data);
572        }
573    }
574  return TRUE;
575}
576
577/* Go up the directory tree from LOCAL_ABSPATH, looking for a versioned
578 * directory.  If found, return its path in *EXISTING_PARENT_ABSPATH.
579 * Otherwise, return SVN_ERR_CLIENT_NO_VERSIONED_PARENT. */
580static svn_error_t *
581find_existing_parent(const char **existing_parent_abspath,
582                     svn_client_ctx_t *ctx,
583                     const char *local_abspath,
584                     apr_pool_t *result_pool,
585                     apr_pool_t *scratch_pool)
586{
587  svn_node_kind_t kind;
588  const char *parent_abspath;
589  svn_wc_context_t *wc_ctx = ctx->wc_ctx;
590
591  SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, local_abspath,
592                            FALSE, FALSE, scratch_pool));
593
594  if (kind == svn_node_dir)
595    {
596      *existing_parent_abspath = apr_pstrdup(result_pool, local_abspath);
597      return SVN_NO_ERROR;
598    }
599
600  if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
601    return svn_error_create(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, NULL);
602
603  if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, scratch_pool),
604                        scratch_pool))
605    return svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED, NULL,
606                             _("'%s' ends in a reserved name"),
607                             svn_dirent_local_style(local_abspath,
608                                                    scratch_pool));
609
610  parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
611
612  if (ctx->cancel_func)
613    SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
614
615  SVN_ERR(find_existing_parent(existing_parent_abspath, ctx, parent_abspath,
616                               result_pool, scratch_pool));
617
618  return SVN_NO_ERROR;
619}
620
621svn_error_t *
622svn_client__get_all_auto_props(apr_hash_t **autoprops,
623                               const char *path_or_url,
624                               svn_client_ctx_t *ctx,
625                               apr_pool_t *result_pool,
626                               apr_pool_t *scratch_pool)
627{
628  int i;
629  apr_array_header_t *inherited_config_auto_props;
630  apr_hash_t *props;
631  svn_opt_revision_t rev;
632  svn_string_t *config_auto_prop;
633  svn_boolean_t use_autoprops;
634  collect_auto_props_baton_t autoprops_baton;
635  svn_error_t *err = NULL;
636  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
637  svn_boolean_t target_is_url = svn_path_is_url(path_or_url);
638  svn_config_t *cfg = ctx->config ? svn_hash_gets(ctx->config,
639                                                  SVN_CONFIG_CATEGORY_CONFIG)
640                                  : NULL;
641  *autoprops = apr_hash_make(result_pool);
642  autoprops_baton.result_pool = result_pool;
643  autoprops_baton.autoprops = *autoprops;
644
645
646  /* Are "traditional" auto-props enabled?  If so grab them from the
647    config.  This is our starting set auto-props, which may be overriden
648    by svn:auto-props. */
649  SVN_ERR(svn_config_get_bool(cfg, &use_autoprops,
650                              SVN_CONFIG_SECTION_MISCELLANY,
651                              SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE));
652  if (use_autoprops)
653    svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS,
654                          all_auto_props_collector, &autoprops_baton,
655                          scratch_pool);
656
657  /* Convert the config file setting (if any) into a hash mapping file
658     patterns to as hash of prop-->val mappings. */
659  if (svn_path_is_url(path_or_url))
660    rev.kind = svn_opt_revision_head;
661  else
662    rev.kind = svn_opt_revision_working;
663
664  /* If PATH_OR_URL is a WC path, then it might be unversioned, in which case
665     we find it's nearest versioned parent. */
666  do
667    {
668      err = svn_client_propget5(&props, &inherited_config_auto_props,
669                                SVN_PROP_INHERITABLE_AUTO_PROPS, path_or_url,
670                                &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
671                                scratch_pool, iterpool);
672      if (err)
673        {
674          if (target_is_url || err->apr_err != SVN_ERR_UNVERSIONED_RESOURCE)
675            return svn_error_trace(err);
676
677          svn_error_clear(err);
678          err = NULL;
679          SVN_ERR(find_existing_parent(&path_or_url, ctx, path_or_url,
680                                       scratch_pool, iterpool));
681        }
682      else
683        {
684          break;
685        }
686    }
687  while (err == NULL);
688
689  /* Stash any explicit PROPS for PARENT_PATH into the inherited props array,
690     since these are actually inherited props for LOCAL_ABSPATH. */
691  config_auto_prop = svn_hash_gets(props, path_or_url);
692
693  if (config_auto_prop)
694    {
695      svn_prop_inherited_item_t *new_iprop =
696        apr_palloc(scratch_pool, sizeof(*new_iprop));
697      new_iprop->path_or_url = path_or_url;
698      new_iprop->prop_hash = apr_hash_make(scratch_pool);
699      svn_hash_sets(new_iprop->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS,
700                    config_auto_prop);
701      APR_ARRAY_PUSH(inherited_config_auto_props,
702                     svn_prop_inherited_item_t *) = new_iprop;
703    }
704
705  for (i = 0; i < inherited_config_auto_props->nelts; i++)
706    {
707      apr_hash_index_t *hi;
708      svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
709        inherited_config_auto_props, i, svn_prop_inherited_item_t *);
710
711      for (hi = apr_hash_first(scratch_pool, elt->prop_hash);
712           hi;
713           hi = apr_hash_next(hi))
714        {
715          const svn_string_t *propval = svn__apr_hash_index_val(hi);
716          const char *ch = propval->data;
717          svn_stringbuf_t *config_auto_prop_pattern;
718          svn_stringbuf_t *config_auto_prop_val;
719
720          svn_pool_clear(iterpool);
721
722          config_auto_prop_pattern = svn_stringbuf_create_empty(iterpool);
723          config_auto_prop_val = svn_stringbuf_create_empty(iterpool);
724
725          /* Parse svn:auto-props value. */
726          while (*ch != '\0')
727            {
728              svn_stringbuf_setempty(config_auto_prop_pattern);
729              svn_stringbuf_setempty(config_auto_prop_val);
730
731              /* Parse the file pattern. */
732              while (*ch != '\0' && *ch != '=' && *ch != '\n')
733                {
734                  svn_stringbuf_appendbyte(config_auto_prop_pattern, *ch);
735                  ch++;
736                }
737
738              svn_stringbuf_strip_whitespace(config_auto_prop_pattern);
739
740              /* Parse the auto-prop group. */
741              while (*ch != '\0' && *ch != '\n')
742                {
743                  svn_stringbuf_appendbyte(config_auto_prop_val, *ch);
744                  ch++;
745                }
746
747              /* Strip leading '=' and whitespace from auto-prop group. */
748              if (config_auto_prop_val->data[0] == '=')
749                svn_stringbuf_remove(config_auto_prop_val, 0, 1);
750              svn_stringbuf_strip_whitespace(config_auto_prop_val);
751
752              all_auto_props_collector(config_auto_prop_pattern->data,
753                                       config_auto_prop_val->data,
754                                       &autoprops_baton,
755                                       scratch_pool);
756
757              /* Skip to next line if any. */
758              while (*ch != '\0' && *ch != '\n')
759                ch++;
760              if (*ch == '\n')
761                ch++;
762            }
763        }
764    }
765
766  svn_pool_destroy(iterpool);
767
768  return SVN_NO_ERROR;
769}
770
771svn_error_t *svn_client__get_inherited_ignores(apr_array_header_t **ignores,
772                                               const char *path_or_url,
773                                               svn_client_ctx_t *ctx,
774                                               apr_pool_t *result_pool,
775                                               apr_pool_t *scratch_pool)
776{
777  svn_opt_revision_t rev;
778  apr_hash_t *explicit_ignores;
779  apr_array_header_t *inherited_ignores;
780  svn_boolean_t target_is_url = svn_path_is_url(path_or_url);
781  svn_string_t *explicit_prop;
782  int i;
783
784  if (target_is_url)
785    rev.kind = svn_opt_revision_head;
786  else
787    rev.kind = svn_opt_revision_working;
788
789  SVN_ERR(svn_client_propget5(&explicit_ignores, &inherited_ignores,
790                              SVN_PROP_INHERITABLE_IGNORES, path_or_url,
791                              &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
792                              scratch_pool, scratch_pool));
793
794  explicit_prop = svn_hash_gets(explicit_ignores, path_or_url);
795
796  if (explicit_prop)
797    {
798      svn_prop_inherited_item_t *new_iprop =
799        apr_palloc(scratch_pool, sizeof(*new_iprop));
800      new_iprop->path_or_url = path_or_url;
801      new_iprop->prop_hash = apr_hash_make(scratch_pool);
802      svn_hash_sets(new_iprop->prop_hash, SVN_PROP_INHERITABLE_IGNORES,
803                    explicit_prop);
804      APR_ARRAY_PUSH(inherited_ignores,
805                     svn_prop_inherited_item_t *) = new_iprop;
806    }
807
808  *ignores = apr_array_make(result_pool, 16, sizeof(const char *));
809
810  for (i = 0; i < inherited_ignores->nelts; i++)
811    {
812      svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
813        inherited_ignores, i, svn_prop_inherited_item_t *);
814      svn_string_t *ignore_val = svn_hash_gets(elt->prop_hash,
815                                               SVN_PROP_INHERITABLE_IGNORES);
816      if (ignore_val)
817        svn_cstring_split_append(*ignores, ignore_val->data, "\n\r\t\v ",
818                                 FALSE, result_pool);
819    }
820
821  return SVN_NO_ERROR;
822}
823
824/* The main logic of the public svn_client_add5.
825 *
826 * EXISTING_PARENT_ABSPATH is the absolute path to the first existing
827 * parent directory of local_abspath. If not NULL, all missing parents
828 * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */
829static svn_error_t *
830add(const char *local_abspath,
831    svn_depth_t depth,
832    svn_boolean_t force,
833    svn_boolean_t no_ignore,
834    svn_boolean_t no_autoprops,
835    const char *existing_parent_abspath,
836    svn_client_ctx_t *ctx,
837    apr_pool_t *scratch_pool)
838{
839  svn_node_kind_t kind;
840  svn_error_t *err;
841  svn_magic__cookie_t *magic_cookie;
842  apr_array_header_t *ignores = NULL;
843
844  svn_magic__init(&magic_cookie, scratch_pool);
845
846  if (existing_parent_abspath)
847    {
848      const char *parent_abspath;
849      const char *child_relpath;
850      apr_array_header_t *components;
851      int i;
852      apr_pool_t *iterpool;
853
854      parent_abspath = existing_parent_abspath;
855      child_relpath = svn_dirent_is_child(existing_parent_abspath,
856                                          local_abspath, NULL);
857      components = svn_path_decompose(child_relpath, scratch_pool);
858      iterpool = svn_pool_create(scratch_pool);
859      for (i = 0; i < components->nelts - 1; i++)
860        {
861          const char *component;
862          svn_node_kind_t disk_kind;
863
864          svn_pool_clear(iterpool);
865
866          if (ctx->cancel_func)
867            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
868
869          component = APR_ARRAY_IDX(components, i, const char *);
870          parent_abspath = svn_dirent_join(parent_abspath, component,
871                                           scratch_pool);
872          SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool));
873          if (disk_kind != svn_node_none && disk_kind != svn_node_dir)
874            return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL,
875                                     _("'%s' prevents creating parent of '%s'"),
876                                     parent_abspath, local_abspath);
877
878          SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool));
879          SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, parent_abspath,
880                                        NULL /*props*/,
881                                        ctx->notify_func2, ctx->notify_baton2,
882                                        scratch_pool));
883        }
884      svn_pool_destroy(iterpool);
885    }
886
887  SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
888  if (kind == svn_node_dir)
889    {
890      /* We use add_dir_recursive for all directory targets
891         and pass depth along no matter what it is, so that the
892         target's depth will be set correctly. */
893      err = add_dir_recursive(local_abspath, depth, force,
894                              no_autoprops, magic_cookie, NULL,
895                              !no_ignore, ignores, ctx,
896                              scratch_pool, scratch_pool);
897    }
898  else if (kind == svn_node_file)
899    err = add_file(local_abspath, magic_cookie, NULL,
900                   no_autoprops, ctx, scratch_pool);
901  else if (kind == svn_node_none)
902    {
903      svn_boolean_t tree_conflicted;
904
905      /* Provide a meaningful error message if the node does not exist
906       * on disk but is a tree conflict victim. */
907      err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted,
908                                 ctx->wc_ctx, local_abspath,
909                                 scratch_pool);
910      if (err)
911        svn_error_clear(err);
912      else if (tree_conflicted)
913        return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
914                                 _("'%s' is an existing item in conflict; "
915                                   "please mark the conflict as resolved "
916                                   "before adding a new item here"),
917                                 svn_dirent_local_style(local_abspath,
918                                                        scratch_pool));
919
920      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
921                               _("'%s' not found"),
922                               svn_dirent_local_style(local_abspath,
923                                                      scratch_pool));
924    }
925  else
926    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
927                             _("Unsupported node kind for path '%s'"),
928                             svn_dirent_local_style(local_abspath,
929                                                    scratch_pool));
930
931  /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set.  */
932  if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
933    {
934      svn_error_clear(err);
935      err = SVN_NO_ERROR;
936    }
937  return svn_error_trace(err);
938}
939
940
941
942svn_error_t *
943svn_client_add5(const char *path,
944                svn_depth_t depth,
945                svn_boolean_t force,
946                svn_boolean_t no_ignore,
947                svn_boolean_t no_autoprops,
948                svn_boolean_t add_parents,
949                svn_client_ctx_t *ctx,
950                apr_pool_t *scratch_pool)
951{
952  const char *parent_abspath;
953  const char *local_abspath;
954  const char *existing_parent_abspath;
955  svn_boolean_t is_wc_root;
956  svn_error_t *err;
957
958  if (svn_path_is_url(path))
959    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
960                             _("'%s' is not a local path"), path);
961
962  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
963
964  /* See if we're being asked to add a wc-root.  That's typically not
965     okay, unless we're in "force" mode.  svn_wc__is_wcroot()
966     will return TRUE even if LOCAL_ABSPATH is a *symlink* to a working
967     copy root, which is a scenario we want to treat differently.  */
968  err = svn_wc__is_wcroot(&is_wc_root, ctx->wc_ctx, local_abspath,
969                          scratch_pool);
970  if (err)
971    {
972      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
973          && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
974        {
975          return svn_error_trace(err);
976        }
977
978      svn_error_clear(err);
979      err = NULL; /* SVN_NO_ERROR */
980      is_wc_root = FALSE;
981    }
982  if (is_wc_root)
983    {
984#ifdef HAVE_SYMLINK
985      svn_node_kind_t disk_kind;
986      svn_boolean_t is_special;
987
988      SVN_ERR(svn_io_check_special_path(local_abspath, &disk_kind, &is_special,
989                                        scratch_pool));
990
991      /* A symlink can be an unversioned target and a wcroot. Lets try to add
992         the symlink, which can't be a wcroot. */
993      if (is_special)
994        is_wc_root = FALSE;
995      else
996#endif
997        {
998          if (! force)
999            return svn_error_createf(
1000                                 SVN_ERR_ENTRY_EXISTS, NULL,
1001                                 _("'%s' is already under version control"),
1002                                 svn_dirent_local_style(local_abspath,
1003                                                        scratch_pool));
1004        }
1005    }
1006
1007  if (is_wc_root)
1008    parent_abspath = local_abspath; /* We will only add children */
1009  else
1010    parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1011
1012  existing_parent_abspath = NULL;
1013  if (add_parents && !is_wc_root)
1014    {
1015      apr_pool_t *subpool;
1016      const char *existing_parent_abspath2;
1017
1018      subpool = svn_pool_create(scratch_pool);
1019      SVN_ERR(find_existing_parent(&existing_parent_abspath2, ctx,
1020                                   parent_abspath, scratch_pool, subpool));
1021      if (strcmp(existing_parent_abspath2, parent_abspath) != 0)
1022        existing_parent_abspath = existing_parent_abspath2;
1023      svn_pool_destroy(subpool);
1024    }
1025
1026  SVN_WC__CALL_WITH_WRITE_LOCK(
1027    add(local_abspath, depth, force, no_ignore, no_autoprops,
1028        existing_parent_abspath, ctx, scratch_pool),
1029    ctx->wc_ctx, (existing_parent_abspath ? existing_parent_abspath
1030                                          : parent_abspath),
1031    FALSE /* lock_anchor */, scratch_pool);
1032  return SVN_NO_ERROR;
1033}
1034
1035
1036static svn_error_t *
1037path_driver_cb_func(void **dir_baton,
1038                    void *parent_baton,
1039                    void *callback_baton,
1040                    const char *path,
1041                    apr_pool_t *pool)
1042{
1043  const svn_delta_editor_t *editor = callback_baton;
1044  SVN_ERR(svn_path_check_valid(path, pool));
1045  return editor->add_directory(path, parent_baton, NULL,
1046                               SVN_INVALID_REVNUM, pool, dir_baton);
1047}
1048
1049/* Append URL, and all it's non-existent parent directories, to TARGETS.
1050   Use TEMPPOOL for temporary allocations and POOL for any additions to
1051   TARGETS. */
1052static svn_error_t *
1053add_url_parents(svn_ra_session_t *ra_session,
1054                const char *url,
1055                apr_array_header_t *targets,
1056                apr_pool_t *temppool,
1057                apr_pool_t *pool)
1058{
1059  svn_node_kind_t kind;
1060  const char *parent_url = svn_uri_dirname(url, pool);
1061
1062  SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool));
1063  SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
1064                            temppool));
1065
1066  if (kind == svn_node_none)
1067    SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool));
1068
1069  APR_ARRAY_PUSH(targets, const char *) = url;
1070
1071  return SVN_NO_ERROR;
1072}
1073
1074static svn_error_t *
1075mkdir_urls(const apr_array_header_t *urls,
1076           svn_boolean_t make_parents,
1077           const apr_hash_t *revprop_table,
1078           svn_commit_callback2_t commit_callback,
1079           void *commit_baton,
1080           svn_client_ctx_t *ctx,
1081           apr_pool_t *pool)
1082{
1083  svn_ra_session_t *ra_session = NULL;
1084  const svn_delta_editor_t *editor;
1085  void *edit_baton;
1086  const char *log_msg;
1087  apr_array_header_t *targets;
1088  apr_hash_t *targets_hash;
1089  apr_hash_t *commit_revprops;
1090  svn_error_t *err;
1091  const char *common;
1092  int i;
1093
1094  /* Find any non-existent parent directories */
1095  if (make_parents)
1096    {
1097      apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts,
1098                                                    sizeof(const char *));
1099      const char *first_url = APR_ARRAY_IDX(urls, 0, const char *);
1100      apr_pool_t *iterpool = svn_pool_create(pool);
1101
1102      SVN_ERR(svn_client_open_ra_session2(&ra_session, first_url, NULL,
1103                                          ctx, pool, iterpool));
1104
1105      for (i = 0; i < urls->nelts; i++)
1106        {
1107          const char *url = APR_ARRAY_IDX(urls, i, const char *);
1108
1109          svn_pool_clear(iterpool);
1110          SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool));
1111        }
1112
1113      svn_pool_destroy(iterpool);
1114
1115      urls = all_urls;
1116    }
1117
1118  /* Condense our list of mkdir targets. */
1119  SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE,
1120                                   pool, pool));
1121
1122  /*Remove duplicate targets introduced by make_parents with more targets. */
1123  SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool));
1124  SVN_ERR(svn_hash_keys(&targets, targets_hash, pool));
1125
1126  if (! targets->nelts)
1127    {
1128      const char *bname;
1129      svn_uri_split(&common, &bname, common, pool);
1130      APR_ARRAY_PUSH(targets, const char *) = bname;
1131
1132      if (*bname == '\0')
1133        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1134                                 _("There is no valid URI above '%s'"),
1135                                 common);
1136    }
1137  else
1138    {
1139      svn_boolean_t resplit = FALSE;
1140
1141      /* We can't "mkdir" the root of an editor drive, so if one of
1142         our targets is the empty string, we need to back everything
1143         up by a path component. */
1144      for (i = 0; i < targets->nelts; i++)
1145        {
1146          const char *path = APR_ARRAY_IDX(targets, i, const char *);
1147          if (! *path)
1148            {
1149              resplit = TRUE;
1150              break;
1151            }
1152        }
1153      if (resplit)
1154        {
1155          const char *bname;
1156
1157          svn_uri_split(&common, &bname, common, pool);
1158
1159          if (*bname == '\0')
1160             return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1161                                      _("There is no valid URI above '%s'"),
1162                                      common);
1163
1164          for (i = 0; i < targets->nelts; i++)
1165            {
1166              const char *path = APR_ARRAY_IDX(targets, i, const char *);
1167              path = svn_relpath_join(bname, path, pool);
1168              APR_ARRAY_IDX(targets, i, const char *) = path;
1169            }
1170        }
1171    }
1172  qsort(targets->elts, targets->nelts, targets->elt_size,
1173        svn_sort_compare_paths);
1174
1175  /* ### This reparent may be problematic in limited-authz-to-common-parent
1176     ### scenarios (compare issue #3242).  See also issue #3649. */
1177  if (ra_session)
1178    SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1179
1180  /* Create new commit items and add them to the array. */
1181  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
1182    {
1183      svn_client_commit_item3_t *item;
1184      const char *tmp_file;
1185      apr_array_header_t *commit_items
1186        = apr_array_make(pool, targets->nelts, sizeof(item));
1187
1188      for (i = 0; i < targets->nelts; i++)
1189        {
1190          const char *path = APR_ARRAY_IDX(targets, i, const char *);
1191
1192          item = svn_client_commit_item3_create(pool);
1193          item->url = svn_path_url_add_component2(common, path, pool);
1194          item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1195          APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1196        }
1197
1198      SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
1199                                      ctx, pool));
1200
1201      if (! log_msg)
1202        return SVN_NO_ERROR;
1203    }
1204  else
1205    log_msg = "";
1206
1207  SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
1208                                           log_msg, ctx, pool));
1209
1210  /* Open an RA session for the URL. Note that we don't have a local
1211     directory, nor a place to put temp files. */
1212  if (!ra_session)
1213    SVN_ERR(svn_client_open_ra_session2(&ra_session, common, NULL, ctx,
1214                                        pool, pool));
1215  else
1216    SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1217
1218
1219  /* Fetch RA commit editor */
1220  SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
1221                        svn_client__get_shim_callbacks(ctx->wc_ctx, NULL,
1222                                                       pool)));
1223  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1224                                    commit_revprops,
1225                                    commit_callback,
1226                                    commit_baton,
1227                                    NULL, TRUE, /* No lock tokens */
1228                                    pool));
1229
1230  /* Call the path-based editor driver. */
1231  err = svn_delta_path_driver2(editor, edit_baton, targets, TRUE,
1232                               path_driver_cb_func, (void *)editor, pool);
1233
1234  if (err)
1235    {
1236      /* At least try to abort the edit (and fs txn) before throwing err. */
1237      return svn_error_compose_create(
1238                err,
1239                editor->abort_edit(edit_baton, pool));
1240    }
1241
1242  /* Close the edit. */
1243  return editor->close_edit(edit_baton, pool);
1244}
1245
1246
1247
1248svn_error_t *
1249svn_client__make_local_parents(const char *path,
1250                               svn_boolean_t make_parents,
1251                               svn_client_ctx_t *ctx,
1252                               apr_pool_t *pool)
1253{
1254  svn_error_t *err;
1255  svn_node_kind_t orig_kind;
1256  SVN_ERR(svn_io_check_path(path, &orig_kind, pool));
1257  if (make_parents)
1258    SVN_ERR(svn_io_make_dir_recursively(path, pool));
1259  else
1260    SVN_ERR(svn_io_dir_make(path, APR_OS_DEFAULT, pool));
1261
1262  /* Should no longer use svn_depth_empty to indicate that only the directory
1263     itself is added, since it not only constraints the operation depth, but
1264     also defines the depth of the target directory now. Moreover, the new
1265     directory will have no children at all.*/
1266  err = svn_client_add5(path, svn_depth_infinity, FALSE, FALSE, FALSE,
1267                        make_parents, ctx, pool);
1268
1269  /* If we created a new directory, but couldn't add it to version
1270     control, then delete it. */
1271  if (err && (orig_kind == svn_node_none))
1272    {
1273      /* ### If this returns an error, should we link it onto
1274         err instead, so that the user is warned that we just
1275         created an unversioned directory? */
1276
1277      svn_error_clear(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
1278    }
1279
1280  return svn_error_trace(err);
1281}
1282
1283
1284svn_error_t *
1285svn_client_mkdir4(const apr_array_header_t *paths,
1286                  svn_boolean_t make_parents,
1287                  const apr_hash_t *revprop_table,
1288                  svn_commit_callback2_t commit_callback,
1289                  void *commit_baton,
1290                  svn_client_ctx_t *ctx,
1291                  apr_pool_t *pool)
1292{
1293  if (! paths->nelts)
1294    return SVN_NO_ERROR;
1295
1296  SVN_ERR(svn_client__assert_homogeneous_target_type(paths));
1297
1298  if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *)))
1299    {
1300      SVN_ERR(mkdir_urls(paths, make_parents, revprop_table, commit_callback,
1301                         commit_baton, ctx, pool));
1302    }
1303  else
1304    {
1305      /* This is a regular "mkdir" + "svn add" */
1306      apr_pool_t *subpool = svn_pool_create(pool);
1307      int i;
1308
1309      for (i = 0; i < paths->nelts; i++)
1310        {
1311          const char *path = APR_ARRAY_IDX(paths, i, const char *);
1312
1313          svn_pool_clear(subpool);
1314
1315          /* See if the user wants us to stop. */
1316          if (ctx->cancel_func)
1317            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1318
1319          SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx,
1320                                                 subpool));
1321        }
1322      svn_pool_destroy(subpool);
1323    }
1324
1325  return SVN_NO_ERROR;
1326}
1327