فهرست منبع

Simplify path handling

Daniele Bartolini 13 سال پیش
والد
کامیت
53bdc3f8d1
2فایلهای تغییر یافته به همراه138 افزوده شده و 120 حذف شده
  1. 74 84
      src/core/strings/Path.h
  2. 64 36
      src/core/strings/String.h

+ 74 - 84
src/core/strings/Path.h

@@ -26,7 +26,6 @@ OTHER DEALINGS IN THE SOFTWARE.
 #pragma once
 
 #include "String.h"
-#include "OS.h"
 
 namespace crown
 {
@@ -41,9 +40,10 @@ void pathname(const char* path, char* str, size_t len);
 void filename(const char* path, char* str, size_t len);
 void basename(const char* path, char* str, size_t len);
 void extension(const char* path, char* str, size_t len);
+void filename_without_extension(const char* path, char* str, size_t len);
 
 //bool segments(const char* path, List<Str>& ret);
-bool remove_trailing_separator(const char* path, char* ret);
+void strip_trailing_separator(const char* path, char* ret, size_t len);
 
 /// Returns whether the segment is valid.
 /// @note
@@ -56,23 +56,30 @@ bool remove_trailing_separator(const char* path, char* ret);
 /// (Thanks org.eclipse.core.runtime for the documentation ;D).
 bool is_valid_segment(const char* segment)
 {
+	assert(segment != NULL);
+	
 	size_t segment_len = string::strlen(segment);
 
+	// Empty segment is not valid
 	if (segment_len == 0)
 	{
 		return false;
 	}
 
+	// Segments containing only '.' are non valid
 	if (segment_len == 1 && segment[0] == '.')
 	{
 		return false;
 	}
 
+	// Segments containing only ".." are not valid
 	if (segment_len == 2 && segment[0] == '.' && segment[1] == '.')
 	{
 		return false;
 	}
 
+	// The segment does not have to contain any forward slashes ('/')
+	// nor back slashes ('\'), nor colon signs (':')
 	for (size_t i = 0; i < segment_len; i++)
 	{
 		if (segment[i] == '/' ||
@@ -142,23 +149,23 @@ bool is_absolute_path(const char* path)
 /// e.g. "/home/project/texture.tga" -> "/home/project"
 /// e.g. "/home/project" -> "/home"
 /// e.g. "/home" -> "/"
+/// e.g. "home" -> ""
+/// 
+/// The @path must be valid.
 void pathname(const char* path, char* str, size_t len)
 {
 	assert(path != NULL);
 	assert(str != NULL);
-	
-	int32_t last_separator = string::find_last(path, '/');
 
-	if (last_separator == -1 || last_separator == 0)
+	const char* last_separator = string::find_last(path, '/');
+
+	if (last_separator == string::end(path))
 	{
 		string::strncpy(str, "", len);
 	}
 	else
 	{
-		size_t final_len = (len >= (size_t)last_separator) ? last_separator : len;
-		
-		string::strncpy(str, path, final_len);
-		str[final_len] = '\0';
+		string::substring(string::begin(path), last_separator, str, len);
 	}
 }
 
@@ -168,35 +175,22 @@ void pathname(const char* path, char* str, size_t len)
 /// e.g. "/home/project/texture" -> "texture"
 /// e.g. "/home -> "home"
 /// e.g. "/" -> ""
+///
+/// The @path must be valid.
 void filename(const char* path, char* str, size_t len)
 {
 	assert(path != NULL);
 	assert(str != NULL);
-	
-	size_t path_len = string::strlen(path);
-
-	int32_t last_separator = string::find_last(path, '/');
 
-	if (last_separator == -1)
+	const char* last_separator = string::find_last(path, '/');
+	
+	if (last_separator == string::end(path))
 	{
-		size_t final_len = (len >= (size_t)last_separator) ? last_separator : len;
-		
-		string::strncpy(str, path, final_len);
-		str[final_len] = '\0';
+		string::strncpy(str, "", len);
 	}
 	else
 	{
-		if (last_separator == 0 && path_len == 1)
-		{
-			string::strncpy(str, "", len);
-		}
-		else
-		{
-			size_t final_len = (len >= (size_t)(path_len - last_separator)) ? (path_len - last_separator) : len;
-			
-			string::strncpy(str, path + last_separator + 1, final_len);
-			str[final_len] = '\0';
-		}
+		string::substring(last_separator + 1, string::end(path), str, len);
 	}
 }
 
@@ -205,47 +199,31 @@ void filename(const char* path, char* str, size_t len)
 /// e.g. "/home/project/texture.tga" -> "texture"
 /// e.g. "/home/project" -> "project"
 /// e.g. "/" -> ""
+///
+/// The @path must be valid.
 void basename(const char* path, char* str, size_t len)
 {
 	assert(path != NULL);
 	assert(str != NULL);
-	
-	size_t path_len = string::strlen(path);
 
-	int32_t last_separator = string::find_last(path, '/');
-	int32_t last_dot = string::find_last(path, '.');
+	const char* last_separator = string::find_last(path, '/');
+	const char* last_dot = string::find_last(path, '.');
 
-	if (last_separator != -1 && last_dot != -1)
+	if (last_separator == string::end(path) && last_dot != string::end(path))
 	{
-		size_t basename_length = path_len - (last_separator + (path_len - last_dot)) - 1;
-		
-		size_t final_len = (len >= (size_t)(basename_length)) ? basename_length : len;
-		
-		string::strncpy(str, path + last_separator + 1, final_len);
-		str[final_len] = '\0';
+		string::substring(string::begin(path), last_dot, str, len);
 	}
-	// "/texture"
-	// "/"
-	else if (last_separator != -1 && last_dot == -1)
+	else if (last_separator != string::end(path) && last_dot == string::end(path))
 	{
-		size_t final_len = (len >= (size_t)(path_len - last_separator)) ? (path_len - last_separator) : len;
-		string::strncpy(str, path + last_separator + 1, final_len);
-		str[final_len] = '\0';
+		string::substring(last_separator + 1, string::end(path), str, len);
 	}
-	// "texture"
-	// ""
-	else if (last_separator == -1 && last_dot == -1)
+	else if (last_separator == string::end(path) && last_dot == string::end(path))
 	{
-		size_t final_len = (len >= (size_t)path_len) ? path_len : len;
-		string::strncpy(str, path, final_len);
-		str[final_len] = '\0';
+		string::strncpy(str, path, len);
 	}
-	// "texture.tga"
-	else if (last_separator == -1 && last_dot != -1)
+	else
 	{
-		size_t final_len = (len >= (size_t)last_dot) ? last_dot : len;
-		string::strncpy(str, path, final_len);
-		str[final_len] = '\0';
+		string::substring(last_separator + 1, last_dot, str, len);
 	}
 }
 
@@ -253,28 +231,41 @@ void basename(const char* path, char* str, size_t len)
 /// @note
 /// e.g. "/home/project/texture.tga" -> "tga"
 /// e.g. "/home/project.x/texture" -> ""
+///
+/// The @path must be valid.
 void extension(const char* path, char* str, size_t len)
 {
 	assert(path != NULL);
 	assert(str != NULL);
-
-	size_t path_len = string::strlen(path);
 	
-	int32_t last_dot = string::find_last(path, '.');
+	const char* last_dot = string::find_last(path, '.');
 	
-	if (last_dot == -1)
+	if (last_dot == string::end(path))
 	{
 		string::strncpy(str, "", len);
 	}
 	else
 	{
-		size_t final_len = (len >= (size_t)(path_len - last_dot)) ? (path_len - last_dot) : len;
-
-		string::strncpy(str, path + last_dot + 1, final_len);
-		str[final_len] = '\0';
+		string::substring(last_dot + 1, string::end(path), str, len);
 	}
 }
 
+/// Returns the filename without the extension.
+/// @note
+/// e.g. "/home/project/texture.tga" -> "/home/project/texture"
+/// e.g. "/home/project/texture" -> "/home/project/texture"
+///
+/// The @path must be valid.
+void filename_without_extension(const char* path, char* str, size_t len)
+{
+	assert(path != NULL);
+	assert(str != NULL);
+	
+	const char* last_dot = string::find_last(path, '.');
+	
+	string::substring(string::begin(path), last_dot, str, len);
+}
+
 /// Returns the segments contained in path.
 //bool segments(const char* path, List<Str>& ret)
 //{
@@ -290,26 +281,25 @@ void extension(const char* path, char* str, size_t len)
 
 /// Fills 'ret' with the same path but without the trailing directory separator.
 /// @note
-/// (e.g. /home/babbeo/texture.tga/ -> /home/babbeo/texture.tga).
-bool remove_trailing_separator(const char* path, char* ret)
+/// e.g. "/home/project/texture.tga/" -> "/home/project/texture.tga"
+/// e.g. "/home/project/texture.tga" -> "/home/project/texture.tga"
+///
+/// The @path must be valid.
+void strip_trailing_separator(const char* path, char* ret, size_t len)
 {
-//	size_t path_len = string::strlen(path);
-//	
-//	if (path_len == 0 || is_root_path(path))
-//	{
-//		strcpy(ret, path);
-//		return true;
-//	}
-
-//	if (path[path_len - 1] == os::PATH_SEPARATOR)
-//	{
-//		string::strncpy(ret, path, path_len - 1);
-//		return true;
-//	}
-
-//	string::strncpy(ret, path, path_len - 1);
-//	return true;
-	return true;
+	assert(path != NULL);
+	assert(ret != NULL);
+	
+	size_t path_len = string::strlen(path);
+	
+	if (path[path_len - 1] == '/')
+	{
+		string::substring(string::begin(path), string::end(path) - 2, ret, len);
+	}
+	else
+	{
+		string::substring(string::begin(path), string::end(path), ret, len);
+	}
 }
 
 } // namespace path

+ 64 - 36
src/core/strings/String.h

@@ -50,8 +50,12 @@ int32_t				strcmp(const char* str1, const char* str2);
 char*				strcpy(char* dest, const char* src);
 char*				strncpy(char* dest, const char* src, size_t len);
 
-int32_t				find_first(const char* str, char c);
-int32_t				find_last(const char* str, char c);
+const char*			begin(const char* str);
+const char*			end(const char* str);
+
+const char*			find_first(const char* str, char c);
+const char*			find_last(const char* str, char c);
+void				substring(const char* begin, const char* end, char* out, size_t len);
 
 //inline void MakeLower()
 //{
@@ -134,25 +138,6 @@ int32_t				find_last(const char* str, char c);
 //	return -1;
 //}
 
-//inline Str GetSubstring(uint32_t start, uint32_t end) const
-//{
-//	assert(start <= mLength);
-//	assert(end <= mLength);
-//	assert(start <= end);
-//	uint32_t len = end - start;
-//	char* tmp = new char[len + 1];
-
-//	for (uint32_t i = 0; i < len; i++)
-//	{
-//		tmp[i] = mText[i + start];
-//	}
-
-//	tmp[len] = '\0';
-//	Str ret(tmp);
-//	delete[] tmp;
-//	return ret;
-//}
-
 //inline void Remove(uint32_t start, uint32_t end)
 //{
 //	assert(start <= mLength);
@@ -356,39 +341,82 @@ inline char* strncpy(char* dest, const char* src, size_t len)
 }
 
 //-----------------------------------------------------------------------------
-inline int32_t find_first(const char* str, char c)
+inline const char* begin(const char* str)
+{
+	assert(str != NULL);
+	
+	return str;
+}
+
+//-----------------------------------------------------------------------------
+inline const char* end(const char* str)
 {
 	assert(str != NULL);
+	
+	return str + string::strlen(str) + 1;
+}
 
-	size_t strLen = string::strlen(str);
+//-----------------------------------------------------------------------------
+inline const char* find_first(const char* str, char c)
+{
+	assert(str != NULL);
 
-	for (size_t i = 0; i < strLen; i++)
+	const char* str_begin = string::begin(str);
+	
+	while (str_begin != string::end(str))
 	{
-		if (str[i] == c)
+		if ((*str_begin) == c)
 		{
-			return i;
+			return str_begin;
 		}
+		
+		str_begin++;
 	}
-
-	return -1;
+	
+	return string::end(str);
 }
 
 //-----------------------------------------------------------------------------
-inline int32_t find_last(const char* str, char c)
+inline const char* find_last(const char* str, char c)
 {
 	assert(str != NULL);
-
-	size_t strLen = string::strlen(str);
-
-	for (size_t i = strLen; i > 0; i--)
+	
+	const char* str_end = string::end(str) - 1;
+	
+	while (str_end != string::begin(str) - 1)
 	{
-		if (str[i - 1] == c)
+		if ((*str_end) == c)
 		{
-			return i - 1;
+			return str_end;
 		}
+		
+		str_end--;
+	}
+	
+	return string::end(str);
+}
+
+//-----------------------------------------------------------------------------
+inline void substring(const char* begin, const char* end, char* out, size_t len)
+{
+	assert(begin != NULL);
+	assert(end != NULL);
+	assert(out != NULL);
+	
+	size_t i = 0;
+	
+	char* out_iterator = out;
+
+	while (begin != end && i < len)
+	{
+		(*out_iterator) = (*begin);
+		
+		begin++;
+		out_iterator++;
+		i++;
 	}
 
-	return -1;
+	out[i] = '\0';
 }
 
 } // namespace string