]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_util/fs-util.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_fs_util / fs-util.c
1 /* fs-util.c : internal utility functions used by both FSFS and BDB back
2  * ends.
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 #include <string.h>
25
26 #include <apr_pools.h>
27 #include <apr_strings.h>
28
29 #include "svn_private_config.h"
30 #include "svn_hash.h"
31 #include "svn_fs.h"
32 #include "svn_dirent_uri.h"
33 #include "svn_path.h"
34 #include "svn_version.h"
35
36 #include "private/svn_fs_util.h"
37 #include "private/svn_fspath.h"
38 #include "private/svn_subr_private.h"
39 #include "../libsvn_fs/fs-loader.h"
40
41
42 const svn_version_t *
43 svn_fs_util__version(void)
44 {
45   SVN_VERSION_BODY;
46 }
47
48
49 /* Return TRUE, if PATH of PATH_LEN > 0 chars starts with a '/' and does
50  * not end with a '/' and does not contain duplicate '/'.
51  */
52 static svn_boolean_t
53 is_canonical_abspath(const char *path, size_t path_len)
54 {
55   const char *end;
56
57   /* check for leading '/' */
58   if (path[0] != '/')
59     return FALSE;
60
61   /* check for trailing '/' */
62   if (path_len == 1)
63     return TRUE;
64   if (path[path_len - 1] == '/')
65     return FALSE;
66
67   /* check for "//" */
68   end = path + path_len - 1;
69   for (; path != end; ++path)
70     if ((path[0] == '/') && (path[1] == '/'))
71       return FALSE;
72
73   return TRUE;
74 }
75
76 svn_boolean_t
77 svn_fs__is_canonical_abspath(const char *path)
78 {
79   /* No PATH?  No problem. */
80   if (! path)
81     return TRUE;
82
83   /* Empty PATH?  That's just "/". */
84   if (! *path)
85     return FALSE;
86
87   /* detailed checks */
88   return is_canonical_abspath(path, strlen(path));
89 }
90
91 const char *
92 svn_fs__canonicalize_abspath(const char *path, apr_pool_t *pool)
93 {
94   char *newpath;
95   size_t path_len;
96   size_t path_i = 0, newpath_i = 0;
97   svn_boolean_t eating_slashes = FALSE;
98
99   /* No PATH?  No problem. */
100   if (! path)
101     return NULL;
102
103   /* Empty PATH?  That's just "/". */
104   if (! *path)
105     return "/";
106
107   /* Non-trivial cases.  Maybe, the path already is canonical after all? */
108   path_len = strlen(path);
109   if (is_canonical_abspath(path, path_len))
110     return apr_pstrmemdup(pool, path, path_len);
111
112   /* Now, the fun begins.  Alloc enough room to hold PATH with an
113      added leading '/'. */
114   newpath = apr_palloc(pool, path_len + 2);
115
116   /* No leading slash?  Fix that. */
117   if (*path != '/')
118     {
119       newpath[newpath_i++] = '/';
120     }
121
122   for (path_i = 0; path_i < path_len; path_i++)
123     {
124       if (path[path_i] == '/')
125         {
126           /* The current character is a '/'.  If we are eating up
127              extra '/' characters, skip this character.  Else, note
128              that we are now eating slashes. */
129           if (eating_slashes)
130             continue;
131           eating_slashes = TRUE;
132         }
133       else
134         {
135           /* The current character is NOT a '/'.  If we were eating
136              slashes, we need not do that any more. */
137           if (eating_slashes)
138             eating_slashes = FALSE;
139         }
140
141       /* Copy the current character into our new buffer. */
142       newpath[newpath_i++] = path[path_i];
143     }
144
145   /* Did we leave a '/' attached to the end of NEWPATH (other than in
146      the root directory case)? */
147   if ((newpath[newpath_i - 1] == '/') && (newpath_i > 1))
148     newpath[newpath_i - 1] = '\0';
149   else
150     newpath[newpath_i] = '\0';
151
152   return newpath;
153 }
154
155 svn_error_t *
156 svn_fs__check_fs(svn_fs_t *fs,
157                  svn_boolean_t expect_open)
158 {
159   if ((expect_open && fs->fsap_data)
160       || ((! expect_open) && (! fs->fsap_data)))
161     return SVN_NO_ERROR;
162   if (expect_open)
163     return svn_error_create(SVN_ERR_FS_NOT_OPEN, 0,
164                             _("Filesystem object has not been opened yet"));
165   else
166     return svn_error_create(SVN_ERR_FS_ALREADY_OPEN, 0,
167                             _("Filesystem object already open"));
168 }
169
170 char *
171 svn_fs__next_entry_name(const char **next_p,
172                         const char *path,
173                         apr_pool_t *pool)
174 {
175   const char *end;
176
177   /* Find the end of the current component.  */
178   end = strchr(path, '/');
179
180   if (! end)
181     {
182       /* The path contains only one component, with no trailing
183          slashes. */
184       *next_p = 0;
185       return apr_pstrdup(pool, path);
186     }
187   else
188     {
189       /* There's a slash after the first component.  Skip over an arbitrary
190          number of slashes to find the next one. */
191       const char *next = end;
192       while (*next == '/')
193         next++;
194       *next_p = next;
195       return apr_pstrndup(pool, path, end - path);
196     }
197 }
198
199 svn_fs_path_change2_t *
200 svn_fs__path_change_create_internal(const svn_fs_id_t *node_rev_id,
201                                     svn_fs_path_change_kind_t change_kind,
202                                     apr_pool_t *pool)
203 {
204   svn_fs_path_change2_t *change;
205
206   change = apr_pcalloc(pool, sizeof(*change));
207   change->node_rev_id = node_rev_id;
208   change->change_kind = change_kind;
209   change->mergeinfo_mod = svn_tristate_unknown;
210   change->copyfrom_rev = SVN_INVALID_REVNUM;
211
212   return change;
213 }
214
215 svn_error_t *
216 svn_fs__append_to_merged_froms(svn_mergeinfo_t *output,
217                                svn_mergeinfo_t input,
218                                const char *rel_path,
219                                apr_pool_t *pool)
220 {
221   apr_hash_index_t *hi;
222
223   *output = apr_hash_make(pool);
224   for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
225     {
226       const char *path = apr_hash_this_key(hi);
227       svn_rangelist_t *rangelist = apr_hash_this_val(hi);
228
229       svn_hash_sets(*output,
230                     svn_fspath__join(path, rel_path, pool),
231                     svn_rangelist_dup(rangelist, pool));
232     }
233
234   return SVN_NO_ERROR;
235 }
236
237 /* Set the version info in *VERSION to COMPAT_MAJOR and COMPAT_MINOR, if
238    the current value refers to a newer version than that.
239  */
240 static void
241 add_compatility(svn_version_t *version,
242                 int compat_major,
243                 int compat_minor)
244 {
245   if (   version->major > compat_major
246       || (version->major == compat_major && version->minor > compat_minor))
247     {
248       version->major = compat_major;
249       version->minor = compat_minor;
250     }
251 }
252
253 svn_error_t *
254 svn_fs__compatible_version(svn_version_t **compatible_version,
255                            apr_hash_t *config,
256                            apr_pool_t *pool)
257 {
258   svn_version_t *version;
259   const char *compatible;
260
261   /* set compatible version according to generic option.
262      Make sure, we are always compatible to the current SVN version
263      (or older). */
264   compatible = svn_hash_gets(config, SVN_FS_CONFIG_COMPATIBLE_VERSION);
265   if (compatible)
266     {
267       SVN_ERR(svn_version__parse_version_string(&version,
268                                                 compatible, pool));
269       add_compatility(version,
270                       svn_subr_version()->major,
271                       svn_subr_version()->minor);
272     }
273   else
274     {
275       version = apr_pmemdup(pool, svn_subr_version(), sizeof(*version));
276     }
277
278   /* specific options take precedence.
279      Let the lowest version compatibility requirement win */
280   if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE))
281     add_compatility(version, 1, 3);
282   else if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE))
283     add_compatility(version, 1, 4);
284   else if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE))
285     add_compatility(version, 1, 5);
286   else if (svn_hash_gets(config, SVN_FS_CONFIG_PRE_1_8_COMPATIBLE))
287     add_compatility(version, 1, 7);
288
289   /* we ignored the patch level and tag so far.
290    * Give them a defined value. */
291   version->patch = 0;
292   version->tag = "";
293
294   /* done here */
295   *compatible_version = version;
296   return SVN_NO_ERROR;
297 }
298
299 svn_boolean_t
300 svn_fs__prop_lists_equal(apr_hash_t *a,
301                          apr_hash_t *b,
302                          apr_pool_t *pool)
303 {
304   apr_hash_index_t *hi;
305
306   /* Quick checks and special cases. */
307   if (a == b)
308     return TRUE;
309
310   if (a == NULL)
311     return apr_hash_count(b) == 0;
312   if (b == NULL)
313     return apr_hash_count(a) == 0;
314
315   if (apr_hash_count(a) != apr_hash_count(b))
316     return FALSE;
317
318   /* Compare prop by prop. */
319   for (hi = apr_hash_first(pool, a); hi; hi = apr_hash_next(hi))
320     {
321       const char *key;
322       apr_ssize_t klen;
323       svn_string_t *val_a, *val_b;
324
325       apr_hash_this(hi, (const void **)&key, &klen, (void **)&val_a);
326       val_b = apr_hash_get(b, key, klen);
327
328       if (!val_b || !svn_string_compare(val_a, val_b))
329         return FALSE;
330     }
331
332   /* No difference found. */
333   return TRUE;
334 }