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