1 /* 2 * ==================================================================== 3 * Copyright (c) 2002-2009 The RapidSvn Group. All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program (in the file GPL.txt. 17 * If not, see <http://www.gnu.org/licenses/>. 18 * 19 * This software consists of voluntary contributions made by many 20 * individuals. For exact contribution history, see the revision 21 * history and logs, available at http://rapidsvn.tigris.org/. 22 * ==================================================================== 23 */ 24 25 // subversion api 26 #include "svn_path.h" 27 #include "svn_dirent_uri.h" 28 29 // apr api 30 #include "apr_file_io.h" 31 32 // svncpp 33 #include "kdevsvncpp/path.hpp" 34 #include "kdevsvncpp/pool.hpp" 35 #include "kdevsvncpp/url.hpp" 36 37 namespace svn 38 { 39 const PathVector EmptyPathVector; 40 Path(const char * path)41 Path::Path(const char * path) 42 { 43 init(path); 44 } 45 Path(const std::string & path)46 Path::Path(const std::string & path) 47 { 48 init(path.c_str()); 49 } 50 Path(const Path & path)51 Path::Path(const Path & path) 52 { 53 init(path.c_str()); 54 } 55 56 void init(const char * path)57 Path::init(const char * path) 58 { 59 Pool pool; 60 61 m_pathIsUrl = false; 62 63 if (path == nullptr) 64 m_path = ""; 65 else 66 { 67 const char * int_path = svn_dirent_canonicalize(path, pool); 68 69 m_path = int_path; 70 71 if (svn::Url::isValid(int_path)) 72 m_pathIsUrl = true; 73 } 74 } 75 76 const std::string & path() const77 Path::path() const 78 { 79 return m_path; 80 } 81 82 const char * c_str() const83 Path::c_str() const 84 { 85 return m_path.c_str(); 86 } 87 88 Path& operator =(const Path & path)89 Path::operator= (const Path & path) 90 { 91 if (this == &path) 92 return *this; 93 94 init(path.c_str()); 95 96 return *this; 97 } 98 99 bool operator ==(const Path & path) const100 Path::operator== (const Path& path) const 101 { 102 if (path.path() == this->path()) 103 return true; 104 105 return false; 106 } 107 108 bool isSet() const109 Path::isSet() const 110 { 111 return m_path.length() > 0; 112 } 113 114 bool isUrl() const115 Path::isUrl() const 116 { 117 return m_pathIsUrl; 118 } 119 120 static bool isAbsolute(const char * path)121 isAbsolute(const char * path) 122 { 123 if (nullptr == path) 124 return false; 125 126 std::string p(path); 127 128 if (0 == p.length()) 129 return false; 130 131 // a path that begins with "/" is absolute 132 if ('/' == p [0]) 133 return true; 134 135 // a path with a ":" like "http://xxx" or 136 // "c:/foo" is absolute too 137 if (p.find(":", 0) != std::string::npos) 138 return true; 139 140 // Well it's relative 141 return false; 142 } 143 144 void addComponent(const char * component)145 Path::addComponent(const char * component) 146 { 147 Pool pool; 148 149 if (nullptr == component) 150 return; 151 152 // in case of an empty string, return 153 if (*component == 0) 154 return; 155 156 // if the @a component is absolute, simply 157 // use it 158 if (isAbsolute(component)) 159 { 160 m_path = component; 161 return; 162 } 163 164 if (Url::isValid(m_path.c_str())) 165 { 166 const char * newPath = 167 svn_path_url_add_component(m_path.c_str(), 168 component, 169 pool); 170 m_path = newPath; 171 } 172 else 173 { 174 svn_stringbuf_t * pathStringbuf = 175 svn_stringbuf_create(m_path.c_str(), pool); 176 177 svn_path_add_component(pathStringbuf, 178 component); 179 180 m_path = pathStringbuf->data; 181 } 182 } 183 184 void addComponent(const std::string & component)185 Path::addComponent(const std::string & component) 186 { 187 addComponent(component.c_str()); 188 } 189 190 void split(std::string & dirpath,std::string & basename) const191 Path::split(std::string & dirpath, std::string & basename) const 192 { 193 Pool pool; 194 195 const char * cdirpath; 196 const char * cbasename; 197 198 svn_path_split(m_path.c_str(), &cdirpath, &cbasename, pool); 199 200 dirpath = cdirpath; 201 basename = cbasename; 202 } 203 204 void split(std::string & dir,std::string & filename,std::string & ext) const205 Path::split(std::string & dir, std::string & filename, std::string & ext) const 206 { 207 std::string basename; 208 209 // first split path into dir and filename+ext 210 split(dir, basename); 211 212 // next search for last . 213 size_t pos = basename.find_last_of("."); 214 if (pos == std::string::npos) 215 { 216 filename = basename; 217 ext = ""; 218 } 219 else 220 { 221 filename = basename.substr(0, pos); 222 ext = basename.substr(pos); 223 } 224 } 225 226 std::string basename() const227 Path::basename() const 228 { 229 std::string dir; 230 std::string filename; 231 232 split(dir, filename); 233 234 return filename; 235 } 236 237 std::string dirpath() const238 Path::dirpath() const 239 { 240 std::string dir; 241 std::string filename; 242 243 split(dir, filename); 244 245 return dir; 246 } 247 248 std::string substr(const size_t count) const249 Path::substr(const size_t count) const 250 { 251 if (m_path.length() > count) 252 return m_path.substr(count); 253 else 254 return ""; 255 } 256 257 std::string unescape() const258 Path::unescape() const 259 { 260 return svn::Url::unescape(m_path.c_str()); 261 } 262 263 264 /* =================================================================== 265 * The next two Fixed_* functions are copies of the APR 266 * apr_temp_dir_get functionality with a fix applied. 267 * This should turn up in APR release 0.9.5 or 1.0, but 268 * for now is reproduced here. 269 * 270 * TODO: Remove this section! 271 */ 272 #include "apr_env.h" 273 274 #define test_tempdir Fixed_test_tempdir 275 #define apr_temp_dir_get Fixed_apr_temp_dir_get 276 277 static char global_temp_dir[APR_PATH_MAX+1] = { 0 }; 278 279 /* Try to open a temporary file in the temporary dir, write to it, 280 and then close it. */ Fixed_test_tempdir(const char * temp_dir,apr_pool_t * p)281 static int Fixed_test_tempdir(const char *temp_dir, apr_pool_t *p) 282 { 283 apr_file_t *dummy_file; 284 // This is the only actual fix - adding the ".XXXXXX"! 285 const char *path = apr_pstrcat(p, temp_dir, "/apr-tmp.XXXXXX", NULL); 286 287 if (apr_file_mktemp(&dummy_file, (char *)path, 0, p) == APR_SUCCESS) { 288 if (apr_file_putc('!', dummy_file) == APR_SUCCESS) { 289 if (apr_file_close(dummy_file) == APR_SUCCESS) { 290 apr_file_remove(path, p); 291 return 1; 292 } 293 } 294 } 295 return 0; 296 } 297 Fixed_apr_temp_dir_get(const char ** temp_dir,apr_pool_t * p)298 static apr_status_t Fixed_apr_temp_dir_get(const char **temp_dir, apr_pool_t *p) 299 { 300 apr_status_t apr_err; 301 const char *try_dirs[] = { "/tmp", "/usr/tmp", "/var/tmp" }; 302 const char *try_envs[] = { "TMP", "TEMP", "TMPDIR" }; 303 char *cwd; 304 size_t i; 305 306 /* Our goal is to find a temporary directory suitable for writing 307 into. We'll only pay the price once if we're successful -- we 308 cache our successful find. Here's the order in which we'll try 309 various paths: 310 311 $TMP 312 $TEMP 313 $TMPDIR 314 "/tmp" 315 "/var/tmp" 316 "/usr/tmp" 317 `pwd` 318 319 NOTE: This algorithm is basically the same one used by Python 320 2.2's tempfile.py module. */ 321 322 /* Try the environment first. */ 323 for (i = 0; i < (sizeof(try_envs) / sizeof(const char *)); i++) { 324 char *value; 325 apr_err = apr_env_get(&value, try_envs[i], p); 326 if ((apr_err == APR_SUCCESS) && value) { 327 apr_size_t len = strlen(value); 328 if (len && (len < APR_PATH_MAX) && test_tempdir(value, p)) { 329 memcpy(global_temp_dir, value, len + 1); 330 goto end; 331 } 332 } 333 } 334 335 /* Next, try a set of hard-coded paths. */ 336 for (i = 0; i < (sizeof(try_dirs) / sizeof(const char *)); i++) { 337 if (test_tempdir(try_dirs[i], p)) { 338 memcpy(global_temp_dir, try_dirs[i], strlen(try_dirs[i]) + 1); 339 goto end; 340 } 341 } 342 343 /* Finally, try the current working directory. */ 344 if (APR_SUCCESS == apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, p)) { 345 if (test_tempdir(cwd, p)) { 346 memcpy(global_temp_dir, cwd, strlen(cwd) + 1); 347 goto end; 348 } 349 } 350 351 end: 352 if (global_temp_dir[0]) { 353 *temp_dir = apr_pstrdup(p, global_temp_dir); 354 return APR_SUCCESS; 355 } 356 return APR_EGENERAL; 357 } 358 /* =================================================================== 359 * End of inserted fixed APR code 360 */ 361 362 Path getTempDir()363 Path::getTempDir() 364 { 365 const char * tempdir = nullptr; 366 Pool pool; 367 368 if (apr_temp_dir_get(&tempdir, pool) != APR_SUCCESS) 369 { 370 tempdir = nullptr; 371 } 372 373 return tempdir; 374 } 375 376 size_t length() const377 Path::length() const 378 { 379 return m_path.length(); 380 } 381 382 std::string native() const383 Path::native() const 384 { 385 if (m_pathIsUrl) 386 { 387 // this converts something like 388 // http://foo/my%20location 389 // to 390 // http://foo/my location 391 return Url::unescape(m_path.c_str()); 392 } 393 else 394 { 395 // On Windows, p://foo/bar will be converted to p:\foo\bar 396 Pool pool; 397 return svn_path_local_style(m_path.c_str(), pool); 398 } 399 } 400 } 401 402 /* ----------------------------------------------------------------- 403 * local variables: 404 * eval: (load-file "../../rapidsvn-dev.el") 405 * end: 406 */ 407