]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/add.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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_sorts_private.h"
52 #include "private/svn_magic.h"
53
54 #include "svn_private_config.h"
55
56
57 \f
58 /*** Code. ***/
59
60 /* Remove leading and trailing white space from a C string, in place. */
61 static void
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
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
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
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 *
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 *
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 *
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
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 *
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 *
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 *
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 *
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 *
985 path_driver_cb_func(void **dir_baton,
986                     void *parent_baton,
987                     void *callback_baton,
988                     const char *path,
989                     apr_pool_t *pool)
990 {
991   const svn_delta_editor_t *editor = callback_baton;
992   SVN_ERR(svn_path_check_valid(path, pool));
993   return editor->add_directory(path, parent_baton, NULL,
994                                SVN_INVALID_REVNUM, pool, dir_baton);
995 }
996
997 /* Append URL, and all it's non-existent parent directories, to TARGETS.
998    Use TEMPPOOL for temporary allocations and POOL for any additions to
999    TARGETS. */
1000 static svn_error_t *
1001 add_url_parents(svn_ra_session_t *ra_session,
1002                 const char *url,
1003                 apr_array_header_t *targets,
1004                 apr_pool_t *temppool,
1005                 apr_pool_t *pool)
1006 {
1007   svn_node_kind_t kind;
1008   const char *parent_url = svn_uri_dirname(url, pool);
1009
1010   SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool));
1011   SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
1012                             temppool));
1013
1014   if (kind == svn_node_none)
1015     SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool));
1016
1017   APR_ARRAY_PUSH(targets, const char *) = url;
1018
1019   return SVN_NO_ERROR;
1020 }
1021
1022 static svn_error_t *
1023 mkdir_urls(const apr_array_header_t *urls,
1024            svn_boolean_t make_parents,
1025            const apr_hash_t *revprop_table,
1026            svn_commit_callback2_t commit_callback,
1027            void *commit_baton,
1028            svn_client_ctx_t *ctx,
1029            apr_pool_t *pool)
1030 {
1031   svn_ra_session_t *ra_session = NULL;
1032   const svn_delta_editor_t *editor;
1033   void *edit_baton;
1034   const char *log_msg;
1035   apr_array_header_t *targets;
1036   apr_hash_t *targets_hash;
1037   apr_hash_t *commit_revprops;
1038   svn_error_t *err;
1039   const char *common;
1040   int i;
1041
1042   /* Find any non-existent parent directories */
1043   if (make_parents)
1044     {
1045       apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts,
1046                                                     sizeof(const char *));
1047       const char *first_url = APR_ARRAY_IDX(urls, 0, const char *);
1048       apr_pool_t *iterpool = svn_pool_create(pool);
1049
1050       SVN_ERR(svn_client_open_ra_session2(&ra_session, first_url, NULL,
1051                                           ctx, pool, iterpool));
1052
1053       for (i = 0; i < urls->nelts; i++)
1054         {
1055           const char *url = APR_ARRAY_IDX(urls, i, const char *);
1056
1057           svn_pool_clear(iterpool);
1058           SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool));
1059         }
1060
1061       svn_pool_destroy(iterpool);
1062
1063       urls = all_urls;
1064     }
1065
1066   /* Condense our list of mkdir targets. */
1067   SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE,
1068                                    pool, pool));
1069
1070   /*Remove duplicate targets introduced by make_parents with more targets. */
1071   SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool));
1072   SVN_ERR(svn_hash_keys(&targets, targets_hash, pool));
1073
1074   if (! targets->nelts)
1075     {
1076       const char *bname;
1077       svn_uri_split(&common, &bname, common, pool);
1078       APR_ARRAY_PUSH(targets, const char *) = bname;
1079
1080       if (*bname == '\0')
1081         return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1082                                  _("There is no valid URI above '%s'"),
1083                                  common);
1084     }
1085   else
1086     {
1087       svn_boolean_t resplit = FALSE;
1088
1089       /* We can't "mkdir" the root of an editor drive, so if one of
1090          our targets is the empty string, we need to back everything
1091          up by a path component. */
1092       for (i = 0; i < targets->nelts; i++)
1093         {
1094           const char *path = APR_ARRAY_IDX(targets, i, const char *);
1095           if (! *path)
1096             {
1097               resplit = TRUE;
1098               break;
1099             }
1100         }
1101       if (resplit)
1102         {
1103           const char *bname;
1104
1105           svn_uri_split(&common, &bname, common, pool);
1106
1107           if (*bname == '\0')
1108              return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1109                                       _("There is no valid URI above '%s'"),
1110                                       common);
1111
1112           for (i = 0; i < targets->nelts; i++)
1113             {
1114               const char *path = APR_ARRAY_IDX(targets, i, const char *);
1115               path = svn_relpath_join(bname, path, pool);
1116               APR_ARRAY_IDX(targets, i, const char *) = path;
1117             }
1118         }
1119     }
1120
1121   svn_sort__array(targets, svn_sort_compare_paths);
1122
1123   /* ### This reparent may be problematic in limited-authz-to-common-parent
1124      ### scenarios (compare issue #3242).  See also issue #3649. */
1125   if (ra_session)
1126     SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1127
1128   /* Create new commit items and add them to the array. */
1129   if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
1130     {
1131       svn_client_commit_item3_t *item;
1132       const char *tmp_file;
1133       apr_array_header_t *commit_items
1134         = apr_array_make(pool, targets->nelts, sizeof(item));
1135
1136       for (i = 0; i < targets->nelts; i++)
1137         {
1138           const char *path = APR_ARRAY_IDX(targets, i, const char *);
1139
1140           item = svn_client_commit_item3_create(pool);
1141           item->url = svn_path_url_add_component2(common, path, pool);
1142           item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1143           APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1144         }
1145
1146       SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
1147                                       ctx, pool));
1148
1149       if (! log_msg)
1150         return SVN_NO_ERROR;
1151     }
1152   else
1153     log_msg = "";
1154
1155   SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
1156                                            log_msg, ctx, pool));
1157
1158   /* Open an RA session for the URL. Note that we don't have a local
1159      directory, nor a place to put temp files. */
1160   if (!ra_session)
1161     SVN_ERR(svn_client_open_ra_session2(&ra_session, common, NULL, ctx,
1162                                         pool, pool));
1163   else
1164     SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1165
1166
1167   /* Fetch RA commit editor */
1168   SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
1169                         svn_client__get_shim_callbacks(ctx->wc_ctx, NULL,
1170                                                        pool)));
1171   SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1172                                     commit_revprops,
1173                                     commit_callback,
1174                                     commit_baton,
1175                                     NULL, TRUE, /* No lock tokens */
1176                                     pool));
1177
1178   /* Call the path-based editor driver. */
1179   err = svn_error_trace(
1180         svn_delta_path_driver2(editor, edit_baton, targets, TRUE,
1181                                path_driver_cb_func, (void *)editor, pool));
1182
1183   if (err)
1184     {
1185       /* At least try to abort the edit (and fs txn) before throwing err. */
1186       return svn_error_compose_create(
1187                 err,
1188                 svn_error_trace(editor->abort_edit(edit_baton, pool)));
1189     }
1190
1191   if (ctx->notify_func2)
1192     {
1193       svn_wc_notify_t *notify;
1194       notify = svn_wc_create_notify_url(common,
1195                                         svn_wc_notify_commit_finalizing,
1196                                         pool);
1197       ctx->notify_func2(ctx->notify_baton2, notify, pool);
1198     }
1199
1200   /* Close the edit. */
1201   return svn_error_trace(editor->close_edit(edit_baton, pool));
1202 }
1203
1204
1205
1206 svn_error_t *
1207 svn_client__make_local_parents(const char *local_abspath,
1208                                svn_boolean_t make_parents,
1209                                svn_client_ctx_t *ctx,
1210                                apr_pool_t *scratch_pool)
1211 {
1212   svn_error_t *err;
1213   svn_node_kind_t orig_kind;
1214   SVN_ERR(svn_io_check_path(local_abspath, &orig_kind, scratch_pool));
1215   if (make_parents)
1216     SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool));
1217   else
1218     SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool));
1219
1220   err = svn_client_add5(local_abspath, svn_depth_empty, FALSE, FALSE, FALSE,
1221                         make_parents, ctx, scratch_pool);
1222
1223   /* If we created a new directory, but couldn't add it to version
1224      control, then delete it. */
1225   if (err && (orig_kind == svn_node_none))
1226     {
1227       err = svn_error_compose_create(err,
1228                                      svn_io_remove_dir2(local_abspath, FALSE,
1229                                                         NULL, NULL,
1230                                                         scratch_pool));
1231     }
1232
1233   return svn_error_trace(err);
1234 }
1235
1236
1237 svn_error_t *
1238 svn_client_mkdir4(const apr_array_header_t *paths,
1239                   svn_boolean_t make_parents,
1240                   const apr_hash_t *revprop_table,
1241                   svn_commit_callback2_t commit_callback,
1242                   void *commit_baton,
1243                   svn_client_ctx_t *ctx,
1244                   apr_pool_t *pool)
1245 {
1246   if (! paths->nelts)
1247     return SVN_NO_ERROR;
1248
1249   SVN_ERR(svn_client__assert_homogeneous_target_type(paths));
1250
1251   if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *)))
1252     {
1253       SVN_ERR(mkdir_urls(paths, make_parents, revprop_table, commit_callback,
1254                          commit_baton, ctx, pool));
1255     }
1256   else
1257     {
1258       /* This is a regular "mkdir" + "svn add" */
1259       apr_pool_t *iterpool = svn_pool_create(pool);
1260       int i;
1261
1262       for (i = 0; i < paths->nelts; i++)
1263         {
1264           const char *path = APR_ARRAY_IDX(paths, i, const char *);
1265
1266           svn_pool_clear(iterpool);
1267
1268           /* See if the user wants us to stop. */
1269           if (ctx->cancel_func)
1270             SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1271
1272           SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool));
1273
1274           SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx,
1275                                                  iterpool));
1276         }
1277       svn_pool_destroy(iterpool);
1278     }
1279
1280   return SVN_NO_ERROR;
1281 }