]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_client/add.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_client / add.c
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 \f
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 \f
57 /*** Code. ***/
58
59 /* Remove leading and trailing white space from a C string, in place. */
60 static void
61 trim_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. */
77 static void
78 unquote_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. */
94 static void
95 split_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 */
151 static void
152 get_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
187 svn_error_t *
188 svn_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. */
268 static svn_error_t *
269 add_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  */
355 static svn_error_t *
356 add_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 */
491 typedef 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 */
506 static svn_boolean_t
507 all_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. */
580 static svn_error_t *
581 find_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
621 svn_error_t *
622 svn_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
771 svn_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. */
829 static svn_error_t *
830 add(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
942 svn_error_t *
943 svn_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
1036 static svn_error_t *
1037 path_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. */
1052 static svn_error_t *
1053 add_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
1074 static svn_error_t *
1075 mkdir_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
1248 svn_error_t *
1249 svn_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
1284 svn_error_t *
1285 svn_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 }