]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_fs_util/fs-util.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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_hash.h"
30 #include "svn_fs.h"
31 #include "svn_dirent_uri.h"
32 #include "svn_path.h"
33 #include "svn_private_config.h"
34
35 #include "private/svn_fs_util.h"
36 #include "private/svn_fspath.h"
37 #include "../libsvn_fs/fs-loader.h"
38
39 /* Return TRUE, if PATH of PATH_LEN > 0 chars starts with a '/' and does
40  * not end with a '/' and does not contain duplicate '/'.
41  */
42 static svn_boolean_t
43 is_canonical_abspath(const char *path, size_t path_len)
44 {
45   const char *end;
46
47   /* check for leading '/' */
48   if (path[0] != '/')
49     return FALSE;
50
51   /* check for trailing '/' */
52   if (path_len == 1)
53     return TRUE;
54   if (path[path_len - 1] == '/')
55     return FALSE;
56
57   /* check for "//" */
58   end = path + path_len - 1;
59   for (; path != end; ++path)
60     if ((path[0] == '/') && (path[1] == '/'))
61       return FALSE;
62
63   return TRUE;
64 }
65
66 svn_boolean_t
67 svn_fs__is_canonical_abspath(const char *path)
68 {
69   /* No PATH?  No problem. */
70   if (! path)
71     return TRUE;
72
73   /* Empty PATH?  That's just "/". */
74   if (! *path)
75     return FALSE;
76
77   /* detailed checks */
78   return is_canonical_abspath(path, strlen(path));
79 }
80
81 const char *
82 svn_fs__canonicalize_abspath(const char *path, apr_pool_t *pool)
83 {
84   char *newpath;
85   size_t path_len;
86   size_t path_i = 0, newpath_i = 0;
87   svn_boolean_t eating_slashes = FALSE;
88
89   /* No PATH?  No problem. */
90   if (! path)
91     return NULL;
92
93   /* Empty PATH?  That's just "/". */
94   if (! *path)
95     return "/";
96
97   /* Non-trivial cases.  Maybe, the path already is canonical after all? */
98   path_len = strlen(path);
99   if (is_canonical_abspath(path, path_len))
100     return apr_pstrmemdup(pool, path, path_len);
101
102   /* Now, the fun begins.  Alloc enough room to hold PATH with an
103      added leading '/'. */
104   newpath = apr_palloc(pool, path_len + 2);
105
106   /* No leading slash?  Fix that. */
107   if (*path != '/')
108     {
109       newpath[newpath_i++] = '/';
110     }
111
112   for (path_i = 0; path_i < path_len; path_i++)
113     {
114       if (path[path_i] == '/')
115         {
116           /* The current character is a '/'.  If we are eating up
117              extra '/' characters, skip this character.  Else, note
118              that we are now eating slashes. */
119           if (eating_slashes)
120             continue;
121           eating_slashes = TRUE;
122         }
123       else
124         {
125           /* The current character is NOT a '/'.  If we were eating
126              slashes, we need not do that any more. */
127           if (eating_slashes)
128             eating_slashes = FALSE;
129         }
130
131       /* Copy the current character into our new buffer. */
132       newpath[newpath_i++] = path[path_i];
133     }
134
135   /* Did we leave a '/' attached to the end of NEWPATH (other than in
136      the root directory case)? */
137   if ((newpath[newpath_i - 1] == '/') && (newpath_i > 1))
138     newpath[newpath_i - 1] = '\0';
139   else
140     newpath[newpath_i] = '\0';
141
142   return newpath;
143 }
144
145 svn_error_t *
146 svn_fs__check_fs(svn_fs_t *fs,
147                  svn_boolean_t expect_open)
148 {
149   if ((expect_open && fs->fsap_data)
150       || ((! expect_open) && (! fs->fsap_data)))
151     return SVN_NO_ERROR;
152   if (expect_open)
153     return svn_error_create(SVN_ERR_FS_NOT_OPEN, 0,
154                             _("Filesystem object has not been opened yet"));
155   else
156     return svn_error_create(SVN_ERR_FS_ALREADY_OPEN, 0,
157                             _("Filesystem object already open"));
158 }
159
160 char *
161 svn_fs__next_entry_name(const char **next_p,
162                         const char *path,
163                         apr_pool_t *pool)
164 {
165   const char *end;
166
167   /* Find the end of the current component.  */
168   end = strchr(path, '/');
169
170   if (! end)
171     {
172       /* The path contains only one component, with no trailing
173          slashes. */
174       *next_p = 0;
175       return apr_pstrdup(pool, path);
176     }
177   else
178     {
179       /* There's a slash after the first component.  Skip over an arbitrary
180          number of slashes to find the next one. */
181       const char *next = end;
182       while (*next == '/')
183         next++;
184       *next_p = next;
185       return apr_pstrndup(pool, path, end - path);
186     }
187 }
188
189 svn_fs_path_change2_t *
190 svn_fs__path_change_create_internal(const svn_fs_id_t *node_rev_id,
191                                     svn_fs_path_change_kind_t change_kind,
192                                     apr_pool_t *pool)
193 {
194   svn_fs_path_change2_t *change;
195
196   change = apr_pcalloc(pool, sizeof(*change));
197   change->node_rev_id = node_rev_id;
198   change->change_kind = change_kind;
199
200   return change;
201 }
202
203 svn_error_t *
204 svn_fs__append_to_merged_froms(svn_mergeinfo_t *output,
205                                svn_mergeinfo_t input,
206                                const char *rel_path,
207                                apr_pool_t *pool)
208 {
209   apr_hash_index_t *hi;
210
211   *output = apr_hash_make(pool);
212   for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
213     {
214       const char *path = svn__apr_hash_index_key(hi);
215       svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
216
217       svn_hash_sets(*output,
218                     svn_fspath__join(path, rel_path, pool),
219                     svn_rangelist_dup(rangelist, pool));
220     }
221
222   return SVN_NO_ERROR;
223 }