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 */ 42static svn_boolean_t 43is_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 66svn_boolean_t 67svn_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 81const char * 82svn_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 145svn_error_t * 146svn_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 160char * 161svn_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 189svn_fs_path_change2_t * 190svn_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 203svn_error_t * 204svn_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} 224