1/*
2 * util.c:  general routines defying categorization; eventually I
3 *          suspect they'll end up in libsvn_subr, but don't want to
4 *          pollute that right now.  Note that nothing in here is
5 *          specific to working copies.
6 *
7 * ====================================================================
8 *    Licensed to the Apache Software Foundation (ASF) under one
9 *    or more contributor license agreements.  See the NOTICE file
10 *    distributed with this work for additional information
11 *    regarding copyright ownership.  The ASF licenses this file
12 *    to you under the Apache License, Version 2.0 (the
13 *    "License"); you may not use this file except in compliance
14 *    with the License.  You may obtain a copy of the License at
15 *
16 *      http://www.apache.org/licenses/LICENSE-2.0
17 *
18 *    Unless required by applicable law or agreed to in writing,
19 *    software distributed under the License is distributed on an
20 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 *    KIND, either express or implied.  See the License for the
22 *    specific language governing permissions and limitations
23 *    under the License.
24 * ====================================================================
25 */
26
27#include <apr_pools.h>
28#include <apr_file_io.h>
29
30#include "svn_io.h"
31#include "svn_types.h"
32#include "svn_error.h"
33#include "svn_dirent_uri.h"
34#include "svn_path.h"
35#include "svn_props.h"
36#include "svn_version.h"
37
38#include "wc.h"   /* just for prototypes of things in this .c file */
39#include "entries.h"
40#include "private/svn_wc_private.h"
41
42#include "svn_private_config.h"
43
44
45svn_error_t *
46svn_wc__ensure_directory(const char *path,
47                         apr_pool_t *pool)
48{
49  svn_node_kind_t kind;
50
51  SVN_ERR(svn_io_check_path(path, &kind, pool));
52
53  if (kind != svn_node_none && kind != svn_node_dir)
54    {
55      /* If got an error other than dir non-existence, then we can't
56         ensure this directory's existence, so just return the error.
57         Might happen if there's a file in the way, for example. */
58      return svn_error_createf(APR_ENOTDIR, NULL,
59                               _("'%s' is not a directory"),
60                               svn_dirent_local_style(path, pool));
61    }
62  else if (kind == svn_node_none)
63    {
64      /* The dir doesn't exist, and it's our job to change that. */
65      SVN_ERR(svn_io_make_dir_recursively(path, pool));
66    }
67  else  /* No problem, the dir already existed, so just leave. */
68    SVN_ERR_ASSERT(kind == svn_node_dir);
69
70  return SVN_NO_ERROR;
71}
72
73/* Return the library version number. */
74const svn_version_t *
75svn_wc_version(void)
76{
77  SVN_VERSION_BODY;
78}
79
80svn_wc_notify_t *
81svn_wc_create_notify(const char *path,
82                     svn_wc_notify_action_t action,
83                     apr_pool_t *pool)
84{
85  svn_wc_notify_t *ret = apr_pcalloc(pool, sizeof(*ret));
86  ret->path = path;
87  ret->action = action;
88  ret->kind = svn_node_unknown;
89  ret->content_state = ret->prop_state = svn_wc_notify_state_unknown;
90  ret->lock_state = svn_wc_notify_lock_state_unknown;
91  ret->revision = SVN_INVALID_REVNUM;
92  ret->old_revision = SVN_INVALID_REVNUM;
93
94  return ret;
95}
96
97svn_wc_notify_t *
98svn_wc_create_notify_url(const char *url,
99                         svn_wc_notify_action_t action,
100                         apr_pool_t *pool)
101{
102  svn_wc_notify_t *ret = svn_wc_create_notify(".", action, pool);
103  ret->url = url;
104
105  return ret;
106}
107
108/* Pool cleanup function to clear an svn_error_t *. */
109static apr_status_t err_cleanup(void *data)
110{
111  svn_error_clear(data);
112
113  return APR_SUCCESS;
114}
115
116svn_wc_notify_t *
117svn_wc_dup_notify(const svn_wc_notify_t *notify,
118                  apr_pool_t *pool)
119{
120  svn_wc_notify_t *ret = apr_palloc(pool, sizeof(*ret));
121
122  *ret = *notify;
123
124  if (ret->path)
125    ret->path = apr_pstrdup(pool, ret->path);
126  if (ret->mime_type)
127    ret->mime_type = apr_pstrdup(pool, ret->mime_type);
128  if (ret->lock)
129    ret->lock = svn_lock_dup(ret->lock, pool);
130  if (ret->err)
131    {
132      ret->err = svn_error_dup(ret->err);
133      apr_pool_cleanup_register(pool, ret->err, err_cleanup,
134                                apr_pool_cleanup_null);
135    }
136  if (ret->changelist_name)
137    ret->changelist_name = apr_pstrdup(pool, ret->changelist_name);
138  if (ret->merge_range)
139    ret->merge_range = svn_merge_range_dup(ret->merge_range, pool);
140  if (ret->url)
141    ret->url = apr_pstrdup(pool, ret->url);
142  if (ret->path_prefix)
143    ret->path_prefix = apr_pstrdup(pool, ret->path_prefix);
144  if (ret->prop_name)
145    ret->prop_name = apr_pstrdup(pool, ret->prop_name);
146  if (ret->rev_props)
147    ret->rev_props = svn_prop_hash_dup(ret->rev_props, pool);
148
149  return ret;
150}
151
152svn_error_t *
153svn_wc_external_item2_create(svn_wc_external_item2_t **item,
154                             apr_pool_t *pool)
155{
156  *item = apr_pcalloc(pool, sizeof(svn_wc_external_item2_t));
157  return SVN_NO_ERROR;
158}
159
160
161svn_wc_external_item2_t *
162svn_wc_external_item2_dup(const svn_wc_external_item2_t *item,
163                          apr_pool_t *pool)
164{
165  svn_wc_external_item2_t *new_item = apr_palloc(pool, sizeof(*new_item));
166
167  *new_item = *item;
168
169  if (new_item->target_dir)
170    new_item->target_dir = apr_pstrdup(pool, new_item->target_dir);
171
172  if (new_item->url)
173    new_item->url = apr_pstrdup(pool, new_item->url);
174
175  return new_item;
176}
177
178
179svn_boolean_t
180svn_wc_match_ignore_list(const char *str, const apr_array_header_t *list,
181                         apr_pool_t *pool)
182{
183  /* For now, we simply forward to svn_cstring_match_glob_list. In the
184     future, if we support more complex ignore patterns, we would iterate
185     over 'list' ourselves, and decide for each pattern how to handle
186     it. */
187
188  return svn_cstring_match_glob_list(str, list);
189}
190
191svn_wc_conflict_description2_t *
192svn_wc_conflict_description_create_text2(const char *local_abspath,
193                                         apr_pool_t *result_pool)
194{
195  svn_wc_conflict_description2_t *conflict;
196
197  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_abspath));
198
199  conflict = apr_pcalloc(result_pool, sizeof(*conflict));
200  conflict->local_abspath = apr_pstrdup(result_pool, local_abspath);
201  conflict->node_kind = svn_node_file;
202  conflict->kind = svn_wc_conflict_kind_text;
203  conflict->action = svn_wc_conflict_action_edit;
204  conflict->reason = svn_wc_conflict_reason_edited;
205  return conflict;
206}
207
208svn_wc_conflict_description2_t *
209svn_wc_conflict_description_create_prop2(const char *local_abspath,
210                                         svn_node_kind_t node_kind,
211                                         const char *property_name,
212                                         apr_pool_t *result_pool)
213{
214  svn_wc_conflict_description2_t *conflict;
215
216  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_abspath));
217
218  conflict = apr_pcalloc(result_pool, sizeof(*conflict));
219  conflict->local_abspath = apr_pstrdup(result_pool, local_abspath);
220  conflict->node_kind = node_kind;
221  conflict->kind = svn_wc_conflict_kind_property;
222  conflict->property_name = apr_pstrdup(result_pool, property_name);
223  return conflict;
224}
225
226svn_wc_conflict_description2_t *
227svn_wc_conflict_description_create_tree2(
228  const char *local_abspath,
229  svn_node_kind_t node_kind,
230  svn_wc_operation_t operation,
231  const svn_wc_conflict_version_t *src_left_version,
232  const svn_wc_conflict_version_t *src_right_version,
233  apr_pool_t *result_pool)
234{
235  svn_wc_conflict_description2_t *conflict;
236
237  SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_abspath));
238
239  conflict = apr_pcalloc(result_pool, sizeof(*conflict));
240  conflict->local_abspath = apr_pstrdup(result_pool, local_abspath);
241  conflict->node_kind = node_kind;
242  conflict->kind = svn_wc_conflict_kind_tree;
243  conflict->operation = operation;
244  conflict->src_left_version = svn_wc_conflict_version_dup(src_left_version,
245                                                           result_pool);
246  conflict->src_right_version = svn_wc_conflict_version_dup(src_right_version,
247                                                            result_pool);
248  return conflict;
249}
250
251
252svn_wc_conflict_description2_t *
253svn_wc__conflict_description2_dup(const svn_wc_conflict_description2_t *conflict,
254                                  apr_pool_t *pool)
255{
256  svn_wc_conflict_description2_t *new_conflict;
257
258  new_conflict = apr_pcalloc(pool, sizeof(*new_conflict));
259
260  /* Shallow copy all members. */
261  *new_conflict = *conflict;
262
263  if (conflict->local_abspath)
264    new_conflict->local_abspath = apr_pstrdup(pool, conflict->local_abspath);
265  if (conflict->property_name)
266    new_conflict->property_name = apr_pstrdup(pool, conflict->property_name);
267  if (conflict->mime_type)
268    new_conflict->mime_type = apr_pstrdup(pool, conflict->mime_type);
269  if (conflict->base_abspath)
270    new_conflict->base_abspath = apr_pstrdup(pool, conflict->base_abspath);
271  if (conflict->their_abspath)
272    new_conflict->their_abspath = apr_pstrdup(pool, conflict->their_abspath);
273  if (conflict->my_abspath)
274    new_conflict->my_abspath = apr_pstrdup(pool, conflict->my_abspath);
275  if (conflict->merged_file)
276    new_conflict->merged_file = apr_pstrdup(pool, conflict->merged_file);
277  if (conflict->src_left_version)
278    new_conflict->src_left_version =
279      svn_wc_conflict_version_dup(conflict->src_left_version, pool);
280  if (conflict->src_right_version)
281    new_conflict->src_right_version =
282      svn_wc_conflict_version_dup(conflict->src_right_version, pool);
283
284  return new_conflict;
285}
286
287svn_wc_conflict_version_t *
288svn_wc_conflict_version_create2(const char *repos_url,
289                                const char *repos_uuid,
290                                const char *repos_relpath,
291                                svn_revnum_t revision,
292                                svn_node_kind_t kind,
293                                apr_pool_t *result_pool)
294{
295  svn_wc_conflict_version_t *version;
296
297  version = apr_pcalloc(result_pool, sizeof(*version));
298
299    SVN_ERR_ASSERT_NO_RETURN(svn_uri_is_canonical(repos_url, result_pool)
300                             && svn_relpath_is_canonical(repos_relpath)
301                             && SVN_IS_VALID_REVNUM(revision)
302                             /* ### repos_uuid can be NULL :( */);
303
304  version->repos_url = repos_url;
305  version->peg_rev = revision;
306  version->path_in_repos = repos_relpath;
307  version->node_kind = kind;
308  version->repos_uuid = repos_uuid;
309
310  return version;
311}
312
313
314svn_wc_conflict_version_t *
315svn_wc_conflict_version_dup(const svn_wc_conflict_version_t *version,
316                            apr_pool_t *result_pool)
317{
318
319  svn_wc_conflict_version_t *new_version;
320
321  if (version == NULL)
322    return NULL;
323
324  new_version = apr_pcalloc(result_pool, sizeof(*new_version));
325
326  /* Shallow copy all members. */
327  *new_version = *version;
328
329  if (version->repos_url)
330    new_version->repos_url = apr_pstrdup(result_pool, version->repos_url);
331
332  if (version->path_in_repos)
333    new_version->path_in_repos = apr_pstrdup(result_pool,
334                                             version->path_in_repos);
335
336  if (version->repos_uuid)
337    new_version->repos_uuid = apr_pstrdup(result_pool, version->repos_uuid);
338
339  return new_version;
340}
341
342
343svn_wc_conflict_description_t *
344svn_wc__cd2_to_cd(const svn_wc_conflict_description2_t *conflict,
345                  apr_pool_t *result_pool)
346{
347  svn_wc_conflict_description_t *new_conflict;
348
349  if (conflict == NULL)
350    return NULL;
351
352  new_conflict = apr_pcalloc(result_pool, sizeof(*new_conflict));
353
354  new_conflict->path = apr_pstrdup(result_pool, conflict->local_abspath);
355  new_conflict->node_kind = conflict->node_kind;
356  new_conflict->kind = conflict->kind;
357  new_conflict->action = conflict->action;
358  new_conflict->reason = conflict->reason;
359  if (conflict->src_left_version)
360    new_conflict->src_left_version =
361          svn_wc_conflict_version_dup(conflict->src_left_version, result_pool);
362  if (conflict->src_right_version)
363    new_conflict->src_right_version =
364          svn_wc_conflict_version_dup(conflict->src_right_version, result_pool);
365
366  switch (conflict->kind)
367    {
368
369      case svn_wc_conflict_kind_property:
370        new_conflict->property_name = apr_pstrdup(result_pool,
371                                                  conflict->property_name);
372        /* Falling through. */
373
374      case svn_wc_conflict_kind_text:
375        new_conflict->is_binary = conflict->is_binary;
376        if (conflict->mime_type)
377          new_conflict->mime_type = apr_pstrdup(result_pool,
378                                                conflict->mime_type);
379        if (conflict->base_abspath)
380          new_conflict->base_file = apr_pstrdup(result_pool,
381                                                conflict->base_abspath);
382        if (conflict->their_abspath)
383          new_conflict->their_file = apr_pstrdup(result_pool,
384                                                 conflict->their_abspath);
385        if (conflict->my_abspath)
386          new_conflict->my_file = apr_pstrdup(result_pool,
387                                              conflict->my_abspath);
388        if (conflict->merged_file)
389          new_conflict->merged_file = apr_pstrdup(result_pool,
390                                                  conflict->merged_file);
391        break;
392
393      case svn_wc_conflict_kind_tree:
394        new_conflict->operation = conflict->operation;
395        break;
396    }
397
398  /* A NULL access baton is allowable by the API. */
399  new_conflict->access = NULL;
400
401  return new_conflict;
402}
403
404
405svn_error_t *
406svn_wc__status2_from_3(svn_wc_status2_t **status,
407                       const svn_wc_status3_t *old_status,
408                       svn_wc_context_t *wc_ctx,
409                       const char *local_abspath,
410                       apr_pool_t *result_pool,
411                       apr_pool_t *scratch_pool)
412{
413  const svn_wc_entry_t *entry = NULL;
414
415  if (old_status == NULL)
416    {
417      *status = NULL;
418      return SVN_NO_ERROR;
419    }
420
421  *status = apr_pcalloc(result_pool, sizeof(**status));
422
423  if (old_status->versioned)
424    {
425      svn_error_t *err;
426      err= svn_wc__get_entry(&entry, wc_ctx->db, local_abspath, FALSE,
427                             svn_node_unknown, result_pool, scratch_pool);
428
429      if (err && err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND)
430        svn_error_clear(err);
431      else
432        SVN_ERR(err);
433    }
434
435  (*status)->entry = entry;
436  (*status)->copied = old_status->copied;
437  (*status)->repos_lock = svn_lock_dup(old_status->repos_lock, result_pool);
438
439  if (old_status->repos_relpath)
440    (*status)->url = svn_path_url_add_component2(old_status->repos_root_url,
441                                                 old_status->repos_relpath,
442                                                 result_pool);
443  (*status)->ood_last_cmt_rev = old_status->ood_changed_rev;
444  (*status)->ood_last_cmt_date = old_status->ood_changed_date;
445  (*status)->ood_kind = old_status->ood_kind;
446  (*status)->ood_last_cmt_author = old_status->ood_changed_author;
447
448  if (old_status->conflicted)
449    {
450      const svn_wc_conflict_description2_t *tree_conflict;
451      SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict, wc_ctx, local_abspath,
452                                        scratch_pool, scratch_pool));
453      (*status)->tree_conflict = svn_wc__cd2_to_cd(tree_conflict, result_pool);
454    }
455
456  (*status)->switched = old_status->switched;
457
458  (*status)->text_status = old_status->node_status;
459  (*status)->prop_status = old_status->prop_status;
460
461  (*status)->repos_text_status = old_status->repos_node_status;
462  (*status)->repos_prop_status = old_status->repos_prop_status;
463
464  /* Some values might be inherited from properties */
465  if (old_status->node_status == svn_wc_status_modified
466      || old_status->node_status == svn_wc_status_conflicted)
467    (*status)->text_status = old_status->text_status;
468
469  /* (Currently a no-op, but just make sure it is ok) */
470  if (old_status->repos_node_status == svn_wc_status_modified
471      || old_status->repos_node_status == svn_wc_status_conflicted)
472    (*status)->repos_text_status = old_status->repos_text_status;
473
474  if (old_status->node_status == svn_wc_status_added)
475    (*status)->prop_status = svn_wc_status_none; /* No separate info */
476
477  /* Find pristine_text_status value */
478  switch (old_status->text_status)
479    {
480      case svn_wc_status_none:
481      case svn_wc_status_normal:
482      case svn_wc_status_modified:
483        (*status)->pristine_text_status = old_status->text_status;
484        break;
485      case svn_wc_status_conflicted:
486      default:
487        /* ### Fetch compare data, or fall back to the documented
488               not retrieved behavior? */
489        (*status)->pristine_text_status = svn_wc_status_none;
490        break;
491    }
492
493  /* Find pristine_prop_status value */
494  switch (old_status->prop_status)
495    {
496      case svn_wc_status_none:
497      case svn_wc_status_normal:
498      case svn_wc_status_modified:
499        if (old_status->node_status != svn_wc_status_added
500            && old_status->node_status != svn_wc_status_deleted
501            && old_status->node_status != svn_wc_status_replaced)
502          {
503            (*status)->pristine_prop_status = old_status->prop_status;
504          }
505        else
506          (*status)->pristine_prop_status = svn_wc_status_none;
507        break;
508      case svn_wc_status_conflicted:
509      default:
510        /* ### Fetch compare data, or fall back to the documented
511               not retrieved behavior? */
512        (*status)->pristine_prop_status = svn_wc_status_none;
513        break;
514    }
515
516  if (old_status->versioned
517      && old_status->conflicted
518      && old_status->node_status != svn_wc_status_obstructed
519      && (old_status->kind == svn_node_file
520          || old_status->node_status != svn_wc_status_missing))
521    {
522      svn_boolean_t text_conflict_p, prop_conflict_p;
523
524      /* The entry says there was a conflict, but the user might have
525         marked it as resolved by deleting the artifact files, so check
526         for that. */
527      SVN_ERR(svn_wc__internal_conflicted_p(&text_conflict_p,
528                                            &prop_conflict_p,
529                                            NULL,
530                                            wc_ctx->db, local_abspath,
531                                            scratch_pool));
532
533      if (text_conflict_p)
534        (*status)->text_status = svn_wc_status_conflicted;
535
536      if (prop_conflict_p)
537        (*status)->prop_status = svn_wc_status_conflicted;
538    }
539
540  return SVN_NO_ERROR;
541}
542
543
544svn_error_t *
545svn_wc__fetch_kind_func(svn_node_kind_t *kind,
546                        void *baton,
547                        const char *path,
548                        svn_revnum_t base_revision,
549                        apr_pool_t *scratch_pool)
550{
551  struct svn_wc__shim_fetch_baton_t *sfb = baton;
552  const char *local_abspath = svn_dirent_join(sfb->base_abspath, path,
553                                              scratch_pool);
554
555  SVN_ERR(svn_wc__db_read_kind(kind, sfb->db, local_abspath,
556                               FALSE /* allow_missing */,
557                               TRUE /* show_deleted */,
558                               FALSE /* show_hidden */,
559                               scratch_pool));
560
561  return SVN_NO_ERROR;
562}
563
564
565svn_error_t *
566svn_wc__fetch_props_func(apr_hash_t **props,
567                         void *baton,
568                         const char *path,
569                         svn_revnum_t base_revision,
570                         apr_pool_t *result_pool,
571                         apr_pool_t *scratch_pool)
572{
573  struct svn_wc__shim_fetch_baton_t *sfb = baton;
574  const char *local_abspath = svn_dirent_join(sfb->base_abspath, path,
575                                              scratch_pool);
576  svn_error_t *err;
577
578  if (sfb->fetch_base)
579    err = svn_wc__db_base_get_props(props, sfb->db, local_abspath, result_pool,
580                                    scratch_pool);
581  else
582    err = svn_wc__db_read_props(props, sfb->db, local_abspath,
583                                result_pool, scratch_pool);
584
585  /* If the path doesn't exist, just return an empty set of props. */
586  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
587    {
588      svn_error_clear(err);
589      *props = apr_hash_make(result_pool);
590    }
591  else if (err)
592    return svn_error_trace(err);
593
594  return SVN_NO_ERROR;
595}
596
597
598svn_error_t *
599svn_wc__fetch_base_func(const char **filename,
600                        void *baton,
601                        const char *path,
602                        svn_revnum_t base_revision,
603                        apr_pool_t *result_pool,
604                        apr_pool_t *scratch_pool)
605{
606  struct svn_wc__shim_fetch_baton_t *sfb = baton;
607  const svn_checksum_t *checksum;
608  svn_error_t *err;
609  const char *local_abspath = svn_dirent_join(sfb->base_abspath, path,
610                                              scratch_pool);
611
612  err = svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
613                                 NULL, NULL, NULL, NULL, &checksum,
614                                 NULL, NULL, NULL, NULL, NULL,
615                                 sfb->db, local_abspath,
616                                 scratch_pool, scratch_pool);
617  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
618    {
619      svn_error_clear(err);
620      *filename = NULL;
621      return SVN_NO_ERROR;
622    }
623  else if (err)
624    return svn_error_trace(err);
625
626  if (checksum == NULL)
627    {
628      *filename = NULL;
629      return SVN_NO_ERROR;
630    }
631
632  SVN_ERR(svn_wc__db_pristine_get_path(filename, sfb->db, local_abspath,
633                                       checksum, scratch_pool, scratch_pool));
634
635  return SVN_NO_ERROR;
636}
637