lean cpp library
A lean C++ library providing efficient utility classes for high-performance C++ applications.
|
00001 /*****************************************************/ 00002 /* lean I/O (c) Tobias Zirr 2011 */ 00003 /*****************************************************/ 00004 00005 #ifndef LEAN_IO_FILESYSTEM 00006 #define LEAN_IO_FILESYSTEM 00007 00008 #include "../lean.h" 00009 #include "../strings/types.h" 00010 #include "../strings/conversions.h" 00011 #include "../strings/string_traits.h" 00012 #include "../meta/strip.h" 00013 #include <algorithm> 00014 00015 #ifndef LEAN_FILESYSTEM_PATH_LENGTH_HINT 00016 00017 00018 #define LEAN_FILESYSTEM_PATH_LENGTH_HINT 256 00019 #endif 00020 00021 namespace lean 00022 { 00023 namespace io 00024 { 00025 00027 LEAN_MAYBE_EXPORT bool file_exists(const utf16_nti& file); 00029 LEAN_INLINE bool file_exists(const utf8_ntri& file) 00030 { 00031 return file_exists(utf_to_utf16(file)); 00032 } 00033 00035 LEAN_MAYBE_EXPORT uint8 file_size(const utf16_nti& file); 00037 LEAN_INLINE uint8 file_size(const utf8_ntri& file) 00038 { 00039 return file_size(utf_to_utf16(file)); 00040 } 00041 00043 LEAN_MAYBE_EXPORT uint8 file_revision(const utf16_nti& file); 00045 LEAN_INLINE uint8 file_revision(const utf8_ntri& file) 00046 { 00047 return file_revision(utf_to_utf16(file)); 00048 } 00049 00053 LEAN_MAYBE_EXPORT size_t current_directory(utf16_t *buffer, size_t bufferSize); 00054 00056 template <class String> 00057 String current_directory(); 00058 00059 #ifndef DOXYGEN_SKIP_THIS 00060 00061 template <> 00062 inline utf16_string current_directory() 00063 { 00064 utf16_string result; 00065 00066 result.resize(LEAN_FILESYSTEM_PATH_LENGTH_HINT); 00067 size_t actualLength = current_directory(&result[0], LEAN_FILESYSTEM_PATH_LENGTH_HINT); 00068 00069 if (actualLength > LEAN_FILESYSTEM_PATH_LENGTH_HINT) 00070 { 00071 result.resize(actualLength); 00072 actualLength = current_directory(&result[0], actualLength); 00073 } 00074 00075 result.erase(actualLength); 00076 return result; 00077 } 00078 00079 template <> 00080 LEAN_INLINE utf8_string current_directory() 00081 { 00082 return strings::utf16_to_utf8<utf8_string>(current_directory<utf16_string>()); 00083 } 00084 00085 template <class String> 00086 LEAN_INLINE String current_directory() 00087 { 00088 return strings::utf_to_utf<String>(current_directory<utf16_string>()); 00089 } 00090 00091 #endif 00092 00094 LEAN_INLINE utf8_string current_directory() 00095 { 00096 return strings::utf16_to_utf8<utf8_string>(current_directory<utf16_string>()); 00097 } 00098 00100 LEAN_INLINE const utf8_string& initial_directory() 00101 { 00102 static const utf8_string initialDir = current_directory<utf8_string>(); 00103 return initialDir; 00104 } 00105 00107 template <class Char> 00108 struct filesystem_chars 00109 { 00111 static const Char root_separator; 00113 static const Char path_separator; 00115 static const Char alt_path_separator; 00117 static const Char extension_separator; 00119 static const Char redirection; 00120 }; 00121 00122 template <class Char> 00123 const Char filesystem_chars<Char>::root_separator = ':'; 00124 template <class Char> 00125 const Char filesystem_chars<Char>::path_separator = '/'; 00126 template <class Char> 00127 const Char filesystem_chars<Char>::alt_path_separator = '\\'; 00128 template <class Char> 00129 const Char filesystem_chars<Char>::extension_separator = '.'; 00130 template <class Char> 00131 const Char filesystem_chars<Char>::redirection = '.'; 00132 00134 template <class Char> 00135 LEAN_INLINE bool is_root_separator(Char chr) 00136 { 00137 return (chr == filesystem_chars<Char>::root_separator); 00138 } 00139 00141 template <class Char> 00142 LEAN_INLINE bool is_alt_path_separator(Char chr) 00143 { 00144 return (chr == filesystem_chars<Char>::alt_path_separator); 00145 } 00146 00148 template <class Char> 00149 LEAN_INLINE bool is_path_separator(Char chr) 00150 { 00151 return (chr == filesystem_chars<Char>::path_separator) || 00152 is_alt_path_separator(chr); 00153 } 00154 00156 template <class Char> 00157 LEAN_INLINE bool is_extension_separator(Char chr) 00158 { 00159 return (chr == filesystem_chars<Char>::extension_separator); 00160 } 00161 00163 template <class Char> 00164 LEAN_INLINE bool is_redirection(Char chr) 00165 { 00166 return (chr == filesystem_chars<Char>::redirection); 00167 } 00168 00170 template <class Char> 00171 LEAN_INLINE Char& assign_path_separator(Char &chr) 00172 { 00173 return (chr = filesystem_chars<Char>::path_separator); 00174 } 00175 00177 template <class Char> 00178 LEAN_INLINE Char& assign_extension_separator(Char &chr) 00179 { 00180 return (chr = filesystem_chars<Char>::extension_separator); 00181 } 00182 00184 template <class Char> 00185 LEAN_INLINE Char& assign_redirection(Char &chr) 00186 { 00187 return (chr = filesystem_chars<Char>::redirection); 00188 } 00189 00191 template <class Char> 00192 LEAN_INLINE Char& canonize_path_separator(Char &chr) 00193 { 00194 if (is_alt_path_separator(chr)) 00195 assign_path_separator(chr); 00196 return chr; 00197 } 00198 00201 template <class String, class Range1, class Range2> 00202 inline typename enable_if_range2<Range1, Range2, String>::type relative_path(const Range1 &base, const Range2 &path) 00203 { 00204 typedef string_traits<String> string_traits; 00205 00206 typename Range1::const_iterator baseMarker = base.begin(), 00207 baseCursor = base.begin(); 00208 typename Range2::const_iterator pathMarker = path.begin(), 00209 pathCursor = path.begin(); 00210 00211 bool endOfBase = (baseCursor == base.end()), 00212 endOfPath = (pathCursor == path.end()); 00213 bool divergent = false; 00214 00215 // Skip identical parts 00216 while (!divergent && !endOfBase && !endOfPath) 00217 { 00218 // Move forward to the end of the next directory name 00219 while (!endOfBase && !is_path_separator(*baseCursor)) 00220 endOfBase = (++baseCursor == base.end()); 00221 while (!endOfPath && !is_path_separator(*pathCursor)) 00222 endOfPath = (++pathCursor == path.end()); 00223 00224 // Compare directory names 00225 divergent = (baseCursor - baseMarker != pathCursor - pathMarker) 00226 || !std::equal(baseMarker, baseCursor, pathMarker); 00227 00228 // Skip path separator 00229 if (!endOfBase) 00230 endOfBase = (++baseCursor == base.end()); 00231 if (!endOfPath) 00232 endOfPath = (++pathCursor == path.end()); 00233 00234 if (!divergent) 00235 { 00236 // Mark beginning of next directory name 00237 baseMarker = baseCursor; 00238 pathMarker = pathCursor; 00239 } 00240 } 00241 00242 // ASSERT: paths equal up to their corresponding markers 00243 00244 String result; 00245 string_traits::reserve( 00246 result, 00247 static_cast<typename string_traits::size_type>(base.end() - baseMarker) 00248 + static_cast<typename string_traits::size_type>(path.end() - pathMarker) 00249 ); 00250 00251 // Slash already skipped, don't lose corresponding directory 00252 int mismatchCount = (baseMarker != base.end()) ? 1 : 0; 00253 00254 // Count mismatching base sub-directories 00255 for (baseCursor = baseMarker; baseCursor != base.end(); ++baseCursor) 00256 if (is_path_separator(*baseCursor)) 00257 ++mismatchCount; 00258 00259 if (mismatchCount != 0) 00260 { 00261 string_traits::resize(result, 3U * mismatchCount - 1U); 00262 typename string_traits::iterator insertCursor = string_traits::begin(result); 00263 00264 // Add redirection for each mismatching sub-directory 00265 for (int i = 0; i < mismatchCount; ++i) 00266 { 00267 if (i != 0) 00268 assign_path_separator(*(insertCursor++)); 00269 assign_redirection(*(insertCursor++)); 00270 assign_redirection(*(insertCursor++)); 00271 } 00272 } 00273 00274 // Append remaining sub-directories 00275 if (pathMarker != path.end()) 00276 { 00277 bool needSeparation = !string_traits::empty(result); 00278 00279 typename string_traits::size_type insertPos = string_traits::size(result); 00280 string_traits::resize(result, insertPos + static_cast<typename string_traits::size_type>(path.end() - pathMarker) + needSeparation); 00281 typename string_traits::iterator insertCursor = string_traits::begin(result) + insertPos; 00282 00283 if (needSeparation) 00284 assign_path_separator(*(insertCursor++)); 00285 00286 insertCursor = std::copy(pathMarker, path.end(), insertCursor); 00287 } 00288 00289 return result; 00290 } 00293 template <class String, class Chars1, class Chars2> 00294 LEAN_INLINE typename enable_if_not_range2<Chars1, Chars2, String>::type relative_path(const Chars1 &base, const Chars2 &path) 00295 { 00296 return relative_path<String>(make_char_range(base), make_char_range(path)); 00297 } 00300 template <class Chars1, class Chars2> 00301 LEAN_INLINE std::basic_string<typename range_char_type2<Chars1, Chars2>::type> relative_path(const Chars1 &base, const Chars2 &path) 00302 { 00303 return relative_path< std::basic_string<typename range_char_type2<Chars1, Chars2>::type> >(make_char_range(base), make_char_range(path)); 00304 } 00305 00307 template <class String, class Range> 00308 inline typename enable_if_range<Range, String>::type canonical_path(const Range &path) 00309 { 00310 typedef string_traits<String> string_traits; 00311 00312 String result; 00313 string_traits::resize(result, path.size()); 00314 00315 typename Range::const_iterator srcCursor = path.end(), 00316 srcMarker = path.end(); 00317 typename string_traits::iterator destCursor = string_traits::end(result); 00318 00319 int skipCounter = 0; 00320 int redirCounter = 0; 00321 00322 bool endOfPath = (srcCursor == path.begin()); 00323 00324 while (!endOfPath) 00325 { 00326 // Pre-decrement, end never dereferenceable 00327 endOfPath = (--srcCursor == path.begin()); 00328 00329 bool isPathSeparator = is_path_separator(*srcCursor); 00330 00331 if (is_redirection(*srcCursor)) 00332 { 00333 // Count redirection characters, as long as no other characters around 00334 if (redirCounter != -1) 00335 ++redirCounter; 00336 } 00337 else if (!isPathSeparator) 00338 // Not a redirection, treat as actual directory name 00339 redirCounter = -1; 00340 00341 // End of current directory 00342 if (isPathSeparator || endOfPath) 00343 { 00344 typename Range::const_iterator srcInsertionCursor = srcCursor; 00345 00346 // Don't copy leading path separators 00347 if (!endOfPath) // == isPathSeparator, except that leading unix-style root slashes are kept 00348 ++srcInsertionCursor; 00349 00350 // Ignore empty directory names & local directory 00351 if (redirCounter != 0 && redirCounter != 1) 00352 { 00353 // Up one directory 00354 if(redirCounter == 2) 00355 // Skip this & also skip next actual directory name 00356 ++skipCounter; 00357 // Actual directory name 00358 else if (skipCounter == 0) 00359 { 00360 // Prepend directory 00361 typename string_traits::iterator newDestCursor = std::copy_backward(srcInsertionCursor, srcMarker, destCursor); 00362 00363 // Replace trailing & leading alternative path separators 00364 canonize_path_separator(*(--destCursor)); 00365 canonize_path_separator(*newDestCursor); 00366 00367 destCursor = newDestCursor; 00368 } 00369 else 00370 // Skip directory 00371 --skipCounter; 00372 } 00373 00374 // Remember insertion cursor, any leading path separator copied as trailing path separator next time 00375 srcMarker = srcInsertionCursor; 00376 00377 redirCounter = 0; 00378 } 00379 00380 } 00381 00382 // Prepend unresolved redirections 00383 while (skipCounter != 0) 00384 { 00385 assign_path_separator(*(--destCursor)); 00386 assign_redirection(*(--destCursor)); 00387 assign_redirection(*(--destCursor)); 00388 --skipCounter; 00389 } 00390 00391 // ASSERT: size of canonical path <= size of source path 00392 00393 // Move canonical path to front 00394 if (destCursor != string_traits::begin(result)) 00395 string_traits::erase(result, string_traits::begin(result), destCursor); 00396 00397 return result; 00398 } 00400 template <class String, class Chars> 00401 LEAN_INLINE typename enable_if_not_range<Chars, String>::type canonical_path(const Chars &path) 00402 { 00403 return canonical_path<String>(make_char_range(path)); 00404 } 00406 template <class Chars> 00407 LEAN_INLINE std::basic_string<typename range_char_type<Chars>::type> canonical_path(const Chars &path) 00408 { 00409 return canonical_path< std::basic_string<typename range_char_type<Chars>::type> >(make_char_range(path)); 00410 } 00411 00413 template <class String, class Range1, class Range2> 00414 inline typename enable_if_range2<Range1, Range2, String>::type append_path(const Range1 &path, const Range2 &file) 00415 { 00416 typedef string_traits<String> string_traits; 00417 00418 String result; 00419 00420 string_traits::resize(result, path.size() + 1 + file.size()); 00421 typename string_traits::iterator insertCursor = string_traits::begin(result); 00422 00423 insertCursor = std::copy(path.begin(), path.end(), insertCursor); 00424 00425 if (!path.empty() && !file.empty() && 00426 !is_path_separator(*(path.end() - 1)) && !is_path_separator(*file.begin())) 00427 assign_path_separator(*(insertCursor++)); 00428 00429 insertCursor = std::copy(file.begin(), file.end(), insertCursor); 00430 00431 string_traits::erase(result, insertCursor, result.end()); 00432 return result; 00433 } 00435 template <class String, class Chars1, class Chars2> 00436 LEAN_INLINE typename enable_if_not_range2<Chars1, Chars2, String>::type append_path(const Chars1 &path, const Chars2 &file) 00437 { 00438 return append_path<String>(make_char_range(path), make_char_range(file)); 00439 } 00441 template <class Chars1, class Chars2> 00442 LEAN_INLINE std::basic_string<typename range_char_type2<Chars1, Chars2>::type> append_path(const Chars1 &path, const Chars2 &file) 00443 { 00444 return append_path< std::basic_string<typename range_char_type2<Chars1, Chars2>::type> >(make_char_range(path), make_char_range(file)); 00445 } 00446 00448 template <class String, class Range1, class Range2> 00449 LEAN_INLINE String absolute_path(const Range1 &base, const Range2 &path) 00450 { 00451 return canonical_path<String>( append_path<String>(make_char_range(base), make_char_range(path)) ); 00452 } 00454 template <class Chars1, class Chars2> 00455 LEAN_INLINE std::basic_string<typename range_char_type2<Chars1, Chars2>::type> absolute_path(const Chars1 &base, const Chars2 &path) 00456 { 00457 typedef std::basic_string<typename range_char_type2<Chars1, Chars2>::type> string_type; 00458 return canonical_path<string_type>( append_path<string_type>(make_char_range(base), make_char_range(path)) ); 00459 } 00460 00462 template <class Range> 00463 inline typename enable_if_range<Range, bool>::type is_rooted(const Range &path) 00464 { 00465 typename Range::const_iterator it = path.begin(); 00466 00467 if (it != path.end()) 00468 { 00469 // Path separator at the beginning indicates root 00470 if (is_path_separator(*it)) 00471 return true; 00472 00473 // Root separator before first path separator indicates root 00474 while (++it != path.end()) 00475 if (is_root_separator(*it)) 00476 return true; 00477 else if (is_path_separator(*it)) 00478 return false; 00479 } 00480 00481 return false; 00482 } 00484 template <class Chars> 00485 LEAN_INLINE typename enable_if_not_range<Chars, bool>::type is_rooted(const Chars &path) 00486 { 00487 return is_rooted( make_char_range(path) ); 00488 } 00489 00491 template <class String, class Range> 00492 inline typename enable_if_range<Range, String>::type absolute_path(const Range &path) 00493 { 00494 String result; 00495 00496 if (!is_rooted(path)) 00497 result = append_path<String>(current_directory<String>(), make_char_range(path)); 00498 else 00499 string_traits<String>::assign(result, path.begin(), path.end()); 00500 00501 return canonical_path<String>(result); 00502 } 00504 template <class String, class Chars> 00505 LEAN_INLINE typename enable_if_not_range<Chars, String>::type absolute_path(const Chars &path) 00506 { 00507 return absolute_path<String>( make_char_range(path) ); 00508 } 00510 template <class Chars> 00511 LEAN_INLINE std::basic_string<typename range_char_type<Chars>::type> absolute_path(const Chars &path) 00512 { 00513 typedef std::basic_string<typename range_char_type<Chars>::type> string_type; 00514 return absolute_path<string_type>( make_char_range(path) ); 00515 } 00516 00518 template <class Iterator> 00519 inline Iterator get_directory(Iterator fileBegin, Iterator fileEnd) 00520 { 00521 Iterator dirEnd = fileEnd; 00522 00523 while (dirEnd != fileBegin) 00524 { 00525 // Pre-decrement, end never dereferenceable 00526 --dirEnd; 00527 00528 // Stop on first path separator, not included 00529 if (is_path_separator(*dirEnd)) 00530 break; 00531 } 00532 00533 return dirEnd; 00534 } 00536 template <class String, class Range> 00537 LEAN_INLINE String get_directory(const Range &file) 00538 { 00539 return string_traits<String>::construct( 00540 file.begin(), 00541 get_directory( file.begin(), file.end() ) 00542 ); 00543 } 00545 template <class String, class Char> 00546 LEAN_INLINE String get_directory(const Char *file) 00547 { 00548 return string_traits<String>::construct( 00549 file, 00550 get_directory( file, file + std::char_traits<Char>::length(file) ) 00551 ); 00552 } 00554 template <class Char> 00555 LEAN_INLINE std::basic_string<Char> get_directory(const Char *file) 00556 { 00557 return std::basic_string<Char>( 00558 file, 00559 get_directory( file, file + std::char_traits<Char>::length(file) ) 00560 ); 00561 } 00562 00564 template <class Iterator> 00565 inline Iterator get_filename(Iterator fileBegin, Iterator fileEnd) 00566 { 00567 Iterator nameBegin = fileEnd; 00568 00569 while (nameBegin != fileBegin) 00570 { 00571 // Pre-decrement, end never dereferenceable 00572 --nameBegin; 00573 00574 // Stop on first path separator 00575 if (is_path_separator(*nameBegin)) 00576 { 00577 // Separator not included 00578 ++nameBegin; 00579 break; 00580 } 00581 } 00582 00583 return nameBegin; 00584 } 00586 template <class String, class Range> 00587 LEAN_INLINE String get_filename(const Range &file) 00588 { 00589 return string_traits<String>::construct( 00590 get_filename( file.begin(), file.end() ), 00591 file.end() 00592 ); 00593 } 00595 template <class Char> 00596 LEAN_INLINE const Char* get_filename(const Char *file) 00597 { 00598 return get_filename( 00599 file, 00600 file + std::char_traits<Char>::length(file) 00601 ); 00602 } 00603 00605 template <class Iterator> 00606 inline range<Iterator> get_stem(Iterator fileBegin, Iterator fileEnd) 00607 { 00608 Iterator nameBegin = fileEnd, stemEnd = fileEnd; 00609 00610 while (nameBegin != fileBegin) 00611 { 00612 // Pre-decrement, end never dereferenceable 00613 --nameBegin; 00614 00615 // Stop on first path separator 00616 if (is_path_separator(*nameBegin)) 00617 { 00618 // Separator not included 00619 ++nameBegin; 00620 break; 00621 } 00622 // Reset stem end on first extension separator 00623 else if (is_extension_separator(*nameBegin) && stemEnd == fileEnd) 00624 stemEnd = nameBegin; 00625 } 00626 00627 return make_range(nameBegin, stemEnd); 00628 } 00630 template <class String, class Range> 00631 LEAN_INLINE String get_stem(const Range &file) 00632 { 00633 return string_from_range<String>( 00634 get_stem( file.begin(), file.end() ) 00635 ); 00636 } 00638 template <class String, class Char> 00639 LEAN_INLINE String get_stem(const Char *file) 00640 { 00641 return string_from_range<String>( 00642 get_stem( file, file + std::char_traits<Char>::length(file) ) 00643 ); 00644 } 00646 template <class Char> 00647 LEAN_INLINE std::basic_string<Char> get_stem(const Char *file) 00648 { 00649 return string_from_range< std::basic_string<Char> >( 00650 get_stem( file, file + std::char_traits<Char>::length(file) ) 00651 ); 00652 } 00653 00655 template <class Iterator> 00656 inline Iterator get_extension(Iterator fileBegin, Iterator fileEnd) 00657 { 00658 Iterator extBegin = fileEnd; 00659 00660 while (extBegin != fileBegin) 00661 { 00662 // Pre-decrement, end never dereferenceable 00663 --extBegin; 00664 00665 // Stop on first extension or stop early on path separator 00666 if (is_extension_separator(*extBegin) || is_path_separator(*extBegin)) 00667 break; 00668 } 00669 00670 // Make sure return value is empty when no extension found 00671 if (extBegin != fileEnd && !is_extension_separator(*extBegin)) 00672 extBegin = fileEnd; 00673 00674 return extBegin; 00675 } 00677 template <class String, class Range> 00678 LEAN_INLINE String get_extension(const Range &file) 00679 { 00680 return string_traits<String>::construct( 00681 get_extension( file.begin(), file.end() ), 00682 file.end() 00683 ); 00684 } 00686 template <class Char> 00687 LEAN_INLINE const Char* get_extension(const Char *file) 00688 { 00689 return get_extension( file, file + std::char_traits<Char>::length(file) ); 00690 } 00691 00692 } // namespace 00693 00694 using io::file_exists; 00695 using io::file_revision; 00696 using io::file_size; 00697 00698 using io::current_directory; 00699 using io::initial_directory; 00700 00701 using io::relative_path; 00702 using io::absolute_path; 00703 using io::canonical_path; 00704 using io::append_path; 00705 00706 using io::get_directory; 00707 using io::get_filename; 00708 using io::get_stem; 00709 using io::get_extension; 00710 00711 } // namespace 00712 00713 #ifdef LEAN_INCLUDE_LINKED 00714 #include "source/filesystem.cpp" 00715 #endif 00716 00717 #endif