Переглянути джерело

T20M5021 #comment 实现并添加fileutil的单元测试

gifur 5 роки тому
батько
коміт
8b5251d90a

+ 6 - 0
libtoolkit/CMakeLists.txt

@@ -17,8 +17,11 @@ file(GLOB ${MODULE_PREFIX}_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp" "*.
 list(REMOVE_ITEM ${MODULE_PREFIX}_SRCS 
     adler32.h
     adler32.c
+    bytestream.h
     cputime.h 
     cputime.c
+    crc32.h
+    crc32.c
     iatpatch.h
     iatpatch.c
     iconv.c
@@ -39,9 +42,12 @@ list(REMOVE_ITEM ${MODULE_PREFIX}_SRCS
     stm.h
     strptime.h # linux confilct and also no appied at windows
     strptime.c
+    waitresult.h
+    waitresult.c
     )
 if(NOT WIN32)
 list(REMOVE_ITEM ${MODULE_PREFIX}_SRCS 
+    dllmain.c
     gettimeofday.c shm.h shm.c
     wavfile.c wavfile.h
     )

+ 9 - 3
libtoolkit/DumpException.h

@@ -1,3 +1,11 @@
+/*
+ * category: [debug]
+ * apply status: framework(spase)
+ * edit status: 
+ * build status:
+ * description: SpBase invoke it at SuppressError function. 
+ */
+
 #ifndef DUMPEXCEPTION_H
 #define DUMPEXCEPTION_H
 
@@ -9,9 +17,7 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
-/*
- * SpBase invoke it at SuppressError function. 
- */
+
 TOOLKIT_API int DumpExceptionInfo(PEXCEPTION_POINTERS pExceptPtrs, HANDLE hLogFile);
 
 #ifdef __cplusplus

+ 8 - 0
libtoolkit/bytestream.h

@@ -1,3 +1,11 @@
+/*
+ * category: [data structure]
+ * apply status: no
+ * edit status:
+ * build status: no
+ * description:
+ */
+
 #ifndef BYTESTREAM_H
 #define BYTESTREAM_H
 

+ 8 - 0
libtoolkit/crc32.h

@@ -1,3 +1,11 @@
+/*
+ * category: [algorithm]
+ * apply status: no used
+ * edit status: not
+ * build status:
+ * description: cut out from zlib
+ */
+
 #ifndef __CRC32_H__
 #define __CRC32_H__
 

+ 109 - 29
libtoolkit/fileutil.c

@@ -1,6 +1,7 @@
 #include "precompile.h"
 #include <wchar.h>
 #include <errno.h>
+#include <assert.h>
 #ifdef _WIN32
 #include <mbstring.h>
 #include <limits.h>
@@ -18,30 +19,65 @@
 
 TOOLKIT_API BOOL CreateDirA(LPCSTR lpDirPath, BOOL bRecursive)
 {
+	char* slashPos = NULL;
+	char* backSlashPos = NULL;
 	size_t len = strlen(lpDirPath);
 	if (len <= 2 || len >= MAX_PATH-2) {
 		return FALSE;
 	} else {
 		if (!bRecursive) {
 			return CreateDirectoryA(lpDirPath, NULL) || (GetLastError() == ERROR_ALREADY_EXISTS);
-		} else {
-			CHAR tmp[MAX_PATH], *p;
-			p = &tmp[0];
+		}
+		else {
+			CHAR tmp[MAX_PATH], * p;
+			CHAR* q = NULL;
+  			q = p = &tmp[0];
 			strncpy(tmp, lpDirPath, MAX_PATH);
-			tmp[MAX_PATH-1] = 0;
-			if (tmp[len-1] != '\\') {
-				tmp[len++] = '\\';
+			tmp[MAX_PATH - 1] = 0;
+			if (tmp[len - 1] != BACK_SLASH && tmp[len - 1] != SLASH) {
+				tmp[len++] = SPLIT_SLASH;
 				tmp[len] = 0;
 			}
-			while ((p = strchr(p, '\\')) != NULL) {
-				*p = 0; 
+#ifdef _WIN32
+
+			do {
+				slashPos = strchr(p, SLASH);
+				backSlashPos = strchr(p, BACK_SLASH);
+				if (slashPos != NULL && backSlashPos != NULL) {
+					p = slashPos < backSlashPos ? slashPos : backSlashPos;
+				}
+				else if (slashPos != NULL) {
+					p = slashPos;
+				}
+				else {
+					p = backSlashPos;
+				}
+				if (!p) {
+					break;
+				}
+				*p = 0;
 				if (!ExistsDirA(tmp)) {
 					if (!CreateDirectoryA(tmp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
 						return FALSE;
 				}
-				*p = '\\';
+				*p = SPLIT_SLASH;
+				p++;
+			}
+			while (true);
+#else
+			while ((p = strchr(p, SPLIT_SLASH)) != NULL) {
+				if(p != q) {//linux compatibility. 
+					*p = 0;
+					if (!ExistsDirA(tmp)) {
+						if (!CreateDirectoryA(tmp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
+							return FALSE;
+					}
+					*p = SPLIT_SLASH;
+				}
 				p++;
+				q = p;
 			}
+#endif
 		}
 	}
 	return TRUE;
@@ -49,6 +85,8 @@ TOOLKIT_API BOOL CreateDirA(LPCSTR lpDirPath, BOOL bRecursive)
 
 TOOLKIT_API BOOL CreateDirW(LPCWSTR lpDirPath, BOOL bRecursive)
 {
+	WCHAR* slashPos = NULL;
+	WCHAR* backSlashPos = NULL;
 	size_t len = wcslen(lpDirPath);
 	if (len <= 2 || len >= MAX_PATH-2) {
 		return FALSE;
@@ -57,22 +95,52 @@ TOOLKIT_API BOOL CreateDirW(LPCWSTR lpDirPath, BOOL bRecursive)
 			return CreateDirectoryW(lpDirPath, NULL) || (GetLastError() == ERROR_ALREADY_EXISTS);
 		} else {
 			WCHAR tmp[MAX_PATH], *p;
-			p = &tmp[0];
+			WCHAR* q = NULL;
+			q = p = &tmp[0];
 			wcsncpy(tmp, lpDirPath, MAX_PATH);
 			tmp[MAX_PATH-1] = 0;
-			if (tmp[len-1] != '\\') {
-				tmp[len++] = '\\';
+			if (tmp[len-1] != '\\' && tmp[len-1] != '/') {
+				tmp[len++] = SPLIT_SLASH;
 				tmp[len] = 0;
 			}
-			while ((p = wcschr(p, '\\')) != NULL) {
-				*p = 0; 
+#ifdef _WIN32
+			do {
+				slashPos = wcschr(p, SLASH);
+				backSlashPos = wcschr(p, BACK_SLASH);
+				if (slashPos != NULL && backSlashPos != NULL) {
+					p = slashPos < backSlashPos ? slashPos : backSlashPos;
+				}
+				else if (slashPos != NULL) {
+					p = slashPos;
+				}
+				else {
+					p = backSlashPos;
+				}
+				if (!p) {
+					break;
+				}
+				*p = 0;
 				if (!ExistsDirW(tmp)) {
 					if (!CreateDirectoryW(tmp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
 						return FALSE;
 				}
-				*p = '\\';
+				*p = SPLIT_SLASH;
+				p++;
+			} while (true);
+#else
+			while ((p = wcschr(p, SPLIT_SLASH)) != NULL) {
+				if (p != q) {//linux compatibility. 
+					*p = 0;
+					if (!ExistsDirW(tmp)) {
+						if (!CreateDirectoryW(tmp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
+							return FALSE;
+					}
+					*p = SPLIT_SLASH;
+				}
 				p++;
+				q = p;
 			}
+#endif
 		}
 	}
 	return TRUE;
@@ -83,8 +151,12 @@ TOOLKIT_API BOOL CreateParentDirA(LPCSTR lpPath, BOOL bRecursive)
 	CHAR pdir[MAX_PATH];
 	const CHAR *p;
 	p = strrchr(lpPath, '\\');
-	if (!p)
+	if (!p) {
+		p = strrchr(lpPath, '/');
+	}
+	if (!p) {
 		return FALSE;
+	}
 	strncpy(pdir, lpPath, p-lpPath);
 	pdir[p-lpPath] = 0;
 	return CreateDirA(pdir, bRecursive);
@@ -95,8 +167,12 @@ TOOLKIT_API BOOL CreateParentDirW(LPCWSTR lpPath, BOOL bRecursive)
 	WCHAR pdir[MAX_PATH];
 	const WCHAR *p;
 	p = wcsrchr(lpPath, '\\');
-	if (!p)
+	if (!p) {
+		p = wcsrchr(lpPath, '/');
+	}
+	if (!p) {
 		return FALSE;
+	}
 	wcsncpy(pdir, lpPath, p-lpPath);
 	pdir[p-lpPath] = 0;
 	return CreateDirW(pdir, bRecursive);
@@ -224,9 +300,9 @@ TOOLKIT_API BOOL CopyDirA(LPCSTR pszSourceDir, LPCSTR pszDestDir)
 			char szDestFile[256] = {0};
 			char *file = ARRAY_IDX(arr, i, char*);			
 			strcpy(szDestFile, pszDestDir);
-			if (szDestFile[strlen(szDestFile)-1] != '\\')
-				strcat(szDestFile, "\\");
-			strcat(szDestFile, strrchr(file, '\\')+1);
+			if (szDestFile[strlen(szDestFile)-1] != '\\' && szDestFile[strlen(szDestFile) - 1] != '/')
+				strcat(szDestFile, MB_SPLIT_SLASH_STR);
+			strcat(szDestFile, strrchr(file, SPLIT_SLASH)+1);
 			
 			bRet &= CopyFileA(file, szDestFile, FALSE);
 		}
@@ -242,9 +318,9 @@ TOOLKIT_API BOOL CopyDirA(LPCSTR pszSourceDir, LPCSTR pszDestDir)
 			char szDestSubDir[256] = {0};
 			char *dir = ARRAY_IDX(arr, i, char*);			
 			strcpy(szDestSubDir, pszDestDir);
-			if (szDestSubDir[strlen(szDestSubDir)-1] != '\\')
-				strcat(szDestSubDir, "\\");
-			strcat(szDestSubDir, strrchr(dir, '\\')+1);
+			if (szDestSubDir[strlen(szDestSubDir)-1] != '\\' && szDestSubDir[strlen(szDestSubDir) - 1] != '/')
+				strcat(szDestSubDir, MB_SPLIT_SLASH_STR);
+			strcat(szDestSubDir, strrchr(dir, SPLIT_SLASH)+1);
 			
 			bRet &= CopyDirA(dir, szDestSubDir);
 		}
@@ -374,7 +450,10 @@ TOOLKIT_API BOOL RemoveDirRecursiveW(LPCWSTR lpDirPath)
 	return bRet;
 }
 
-
+/*
+ * isfile: only fetch file type elem or not.
+ * limitation: max elem count to satsfy.
+ */
 static array_header_t *fileutil_get_sub_a(const char *path, int isfile, int limitation)
 {
 	array_header_t *arr = NULL;
@@ -388,8 +467,8 @@ static array_header_t *fileutil_get_sub_a(const char *path, int isfile, int limi
 		return NULL;
 
 	strcpy(tmp, path);
-	if (tmp[npath-1] != '\\') {
-		tmp[npath++] = '\\';
+	if (tmp[npath-1] != '\\' && tmp[npath - 1] != '/') {
+		tmp[npath++] = SPLIT_SLASH;
 		tmp[npath] = 0;
 	}
 	tmp[npath++] = '*';
@@ -405,9 +484,10 @@ static array_header_t *fileutil_get_sub_a(const char *path, int isfile, int limi
 				int isdir = !!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
 				if (isdir) {
 					if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) {
-						continue;;
+						continue;
 					}
 				}
+				/*[if require file while is not dir] or [not require file but is dir]*/
 				if (isfile ^ isdir) {
 					char *v = malloc(sizeof(char)*(npath + strlen(fd.cFileName)+1));
 					strcpy(v, tmp);
@@ -435,8 +515,8 @@ static array_header_t *fileutil_get_sub_w(const wchar_t *path, int isfile, int l
 		return NULL;
 
 	wcscpy(tmp, path);
-	if (tmp[npath-1] != '\\') {
-		tmp[npath++] = '\\';
+	if (tmp[npath-1] != '\\' && tmp[npath - 1] != '/') {
+		tmp[npath++] = SPLIT_SLASH;
 		tmp[npath] = 0;
 	}
 	tmp[npath++] = '*';

+ 29 - 0
libtoolkit/fileutil.h

@@ -6,6 +6,35 @@
 #include "config.h"
 #include <winpr/file.h>
 
+#define SLASH '/'
+#define BACK_SLASH '\\'
+
+#define MB_SLASH_STR "/"
+#define MB_BACK_SLASH_STR "\\"
+
+#define W_SLASH_STR L"/"
+#define W_BACK_SLASH_STR L"\\"
+
+#if defined(UNICODE) || defined(_UNICODE)
+#define SLASH_STR W_SLASH_STR
+#define BACK_SLASH_STR W_BACK_SLASH_STR
+#else 
+#define SLASH_STR MB_SLASH_STR
+#define BACK_SLASH_STR MB_BACK_SLASH_STR
+#endif
+
+#ifdef _WIN32
+#define SPLIT_SLASH BACK_SLASH
+#define SPLIT_SLASH_STR BACK_SLASH_STR
+#define MB_SPLIT_SLASH_STR MB_BACK_SLASH_STR
+#define W_SPLIT_SLASH_STR   W_BACK_SLASH_STR
+#else
+#define SPLIT_SLASH SLASH
+#define SPLIT_SLASH_STR SLASH_STR
+#define MB_SPLIT_SLASH_STR MB_SLASH_STR
+#define W_SPLIT_SLASH_STR   W_SLASH_STR
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 3 - 0
libtoolkit/strutil.c

@@ -5,9 +5,12 @@
 #include <ctype.h>
 #include <stdarg.h>
 #include <malloc.h>
+#include <assert.h>
+#include <string.h>
 
 #include "strutil.h"
 #include <winpr/wtypes.h>
+#include <winpr/string.h>
 
 #pragma warning(disable : 4311)
 

+ 26 - 0
libtoolkit/test/CMakeLists.txt

@@ -36,6 +36,28 @@ list(APPEND comm_test_libraries libtoolkit_a)
 
 # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS")
 
+if(NOT MSVC)
+	set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME}_area")
+else()
+	set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${MODULE_NAME}_area")
+endif()
+file(MAKE_DIRECTORY "${TEST_AREA}")
+file(WRITE "${TEST_AREA}/test_file1.txt" "I am test file 1.")
+file(WRITE "${TEST_AREA}/test_file2.txt" "I am test file 2.")
+file(WRITE "${TEST_AREA}/test_file3.txt" "I am test file 3.")
+file(MAKE_DIRECTORY "${TEST_AREA}/test_dir_1")
+file(WRITE "${TEST_AREA}/test_dir_1/test_dir_1_file_1.txt" "I am test file 1 under dir 1.")
+file(MAKE_DIRECTORY "${TEST_AREA}/test_dir_2")
+file(WRITE "${TEST_AREA}/test_dir_2/test_dir_2_file_1.txt" "I am test file 1 under dir 2.")
+file(WRITE "${TEST_AREA}/test_dir_2/test_dir_2_file_2.txt" "I am test file 2 under dir 2.")
+file(MAKE_DIRECTORY "${TEST_AREA}/test_dir_2/test_sub_dir_2")
+file(WRITE "${TEST_AREA}/test_dir_2/test_sub_dir_2/test_sub_dir_2_file_1.txt" "I am test file 1 under sub dir 2.")
+file(MAKE_DIRECTORY "${TEST_AREA}/test_dir_3")
+file(WRITE "${TEST_AREA}/test_dir_3/test_dir_3_file_1.txt" "I am test file 1 under dir 3.")
+file(WRITE "${TEST_AREA}/test_dir_3/test_dir_3_file_2.txt" "I am test file 2 under dir 3.")
+file(WRITE "${TEST_AREA}/test_dir_3/test_dir_3_file_3.txt" "I am test file 3 under dir 3.")
+file(MAKE_DIRECTORY "${TEST_AREA}/test_dir_4")
+
 foreach(test ${${MODULE_PREFIX}_TESTS})
 	get_filename_component(test_name ${test} NAME_WE)
 	add_executable(${test_name} ${test})
@@ -44,7 +66,11 @@ foreach(test ${${MODULE_PREFIX}_TESTS})
 	# 会将 测试的执行程序生成在指定的文件夹,并且带上编译的配置条件比如 Debug
 	set_target_properties(${test_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
 	message(STATUS "add test case: ${test_name}...")
+	if(${test_name} STREQUAL "test_file")
+	add_test(NAME ${test_name} COMMAND ${TESTING_OUTPUT_DIRECTORY}/${test_name}  ${TEST_AREA})
+	else()
 	add_test(NAME ${test_name} COMMAND ${TESTING_OUTPUT_DIRECTORY}/${test_name})
+	endif()
 	set_tests_properties(${teset_name} PROPERTIES FAIL_REGULAR_EXPRESSION "FAILED;ERROR;Failed;error")
 endforeach()
 

+ 205 - 0
libtoolkit/test/test_file.cpp

@@ -0,0 +1,205 @@
+#include <gtest/gtest.h>
+#include "fileutil.h"
+#include "array.h"
+
+
+TEST(FileUtilTest, TestGetSubFile)
+{
+	const auto args = ::testing::internal::GetArgvs();
+	EXPECT_FALSE(args.empty());
+	std::string test_dir("");
+	if (args.size() == 1) {
+		auto dir = args[0].find_last_of('/');
+		if (dir != std::string::npos) {
+			test_dir = args[0].substr(0, dir + 1);
+			test_dir += "test_libtoolkit_area";
+		}
+		ASSERT_TRUE(!test_dir.empty());
+	}
+	else {
+		test_dir = args[1];
+	}
+	std::cout << "test dir: " << test_dir << std::endl;
+
+	auto files = fileutil_get_sub_files_a((test_dir + "/test_dir_4").c_str());
+	ASSERT_TRUE(array_empty(files));
+
+	files = fileutil_get_sub_files_a(NULL);
+	ASSERT_TRUE(files == NULL);
+	files = fileutil_get_sub_files_a("C");
+	ASSERT_TRUE(files == NULL);
+
+	files = fileutil_get_sub_dirs_a(test_dir.c_str());
+	ASSERT_TRUE(files != NULL);
+	EXPECT_TRUE(files->nelts == 4);
+	toolkit_array_free2(files);
+
+	files = fileutil_get_sub_dirs2_a(test_dir.c_str(), 2);
+	ASSERT_TRUE(files != NULL);
+	EXPECT_TRUE(files->nelts == 2);
+	toolkit_array_free2(files);
+
+	files = fileutil_get_sub_files_a((test_dir + "/test_dir_3").c_str());
+	ASSERT_TRUE(files != NULL);
+	EXPECT_TRUE(files->nelts == 3);
+	toolkit_array_free2(files);
+
+	files = fileutil_get_sub_files2_a((test_dir + "/test_dir_3").c_str(), 2);
+	EXPECT_TRUE(files->nelts == 2);
+	toolkit_array_free2(files);
+
+	files = fileutil_get_sub_dirs_a((test_dir + "/test_dir_3").c_str());
+	ASSERT_TRUE(array_empty(files));
+
+	files = fileutil_get_sub_dirs2_a((test_dir + "/test_dir_3").c_str(), 2);
+	ASSERT_TRUE(array_empty(files));
+}
+
+#if 0
+/*
+D:\GitReposity\Framework\out\build\x86-Debug\bin\Debug\test_libtoolkit_area
+ */
+TEST(FileUtilTest, DealWithBackSlash)
+{
+	const auto args = ::testing::internal::GetArgvs();
+	EXPECT_FALSE(args.empty());
+	const auto test_dir = args[1];
+	std::cout << "test dir: " << test_dir << std::endl;
+	ASSERT_TRUE(ExistsDirA(test_dir.c_str()));
+	ASSERT_TRUE(ExistsFileA((test_dir + "\\test_file3.txt").c_str()));
+	ASSERT_TRUE(ExistsFileA((test_dir + "\\test_dir_3\\test_dir_3_file_3.txt").c_str()));
+
+	/*copy dir*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_1").c_str()));
+	ASSERT_TRUE(CopyDirA((test_dir + "\\test_dir_3").c_str(), (test_dir + "\\new_dir_1").c_str()));
+	EXPECT_TRUE(ExistsDirA((test_dir + "\\new_dir_1").c_str()));
+	ASSERT_TRUE(RemoveDirRecursiveA((test_dir + "\\new_dir_1").c_str()));
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_1").c_str()));
+
+	/*backup*/
+	CopyDirA((test_dir + "\\test_dir_2").c_str(), (test_dir + "\\new_bk_2").c_str());
+	CopyDirA((test_dir + "\\test_dir_3").c_str(), (test_dir + "\\new_bk_3").c_str());
+
+	/*remove file*/
+	ASSERT_TRUE(RemoveFileA((test_dir + "\\test_dir_3\\test_dir_3_file_3.txt").c_str()));
+	EXPECT_FALSE(ExistsFileA((test_dir + "\\test_dir_3\\test_dir_3_file_3.txt").c_str()));
+
+	/*rollback new_dir_3*/
+	CopyDirA((test_dir + "\\new_bk_3").c_str(), (test_dir + "\\test_dir_3").c_str());
+	RemoveDirRecursiveA((test_dir + "\\new_bk_3").c_str());
+
+	/*create grandson directory*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_2\\new_dir_2_1").c_str()));
+	EXPECT_FALSE(CreateDirA((test_dir + "\\new_dir_2\\new_dir_2_1").c_str(), FALSE));
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_2").c_str()));
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_2\\new_dir_2_1").c_str()));
+	EXPECT_TRUE(CreateDirA((test_dir + "\\new_dir_2\\new_dir_2_1").c_str(), TRUE));
+	EXPECT_TRUE(ExistsDirA((test_dir + "\\new_dir_2\\new_dir_2_1").c_str()));
+	/*remove dir*/
+	ASSERT_TRUE(RemoveDirRecursiveA((test_dir + "\\new_dir_2").c_str()));
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_2\\new_dir_2_1").c_str()));
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_2").c_str()));
+	/*create dir*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_3").c_str()));
+	EXPECT_TRUE(CreateDirRecursiveA((test_dir + "\\new_dir_3\\new_dir_3_1").c_str()));
+	EXPECT_TRUE(ExistsDirA((test_dir + "\\new_dir_3\\new_dir_3_1").c_str()));
+	RemoveDirRecursiveA((test_dir + "\\new_dir_3").c_str());
+	/*create parent dir*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_4").c_str()));
+	EXPECT_FALSE(CreateParentDirA((test_dir + "\\new_dir_4\\new_dir_4_1\\new_dir_4_1_1").c_str(), FALSE));
+	EXPECT_FALSE(ExistsDirA((test_dir + "\\new_dir_4").c_str()));
+	EXPECT_TRUE(CreateParentDirA((test_dir + "\\new_dir_4\\new_dir_4_1\\new_dir_4_1_1").c_str(), TRUE));
+	EXPECT_TRUE(ExistsDirA((test_dir + "\\new_dir_4\\new_dir_4_1").c_str()));
+	RemoveDirRecursiveA((test_dir + "\\new_dir_4").c_str());
+	/*clear dir*/
+	EXPECT_TRUE(ExistsFileA((test_dir + "\\test_dir_2\\test_sub_dir_2\\test_sub_dir_2_file_1.txt").c_str()));
+	EXPECT_TRUE(ClearDirRecursiveA((test_dir + "\\test_dir_2").c_str()));
+	EXPECT_FALSE(ExistsFileA((test_dir + "\\test_dir_2\\test_sub_dir_2\\test_sub_dir_2_file_1.txt").c_str()));
+	EXPECT_TRUE(ExistsDirA((test_dir + "\\test_dir_2\\test_sub_dir_2").c_str()));
+
+	/*rollback new_dir_2*/
+	CopyDirA((test_dir + "\\new_bk_2").c_str(), (test_dir + "\\test_dir_2").c_str());
+	RemoveDirRecursiveA((test_dir + "\\new_bk_2").c_str());
+}
+
+#else
+/*
+D:/GitReposity/Framework/out/build/x86-Debug/bin/Debug/test_libtoolkit_area
+ */
+TEST(FileUtilTest, DealWithSlash)
+{
+	const auto args = ::testing::internal::GetArgvs();
+	EXPECT_FALSE(args.empty());
+	std::string test_dir("");
+	if (args.size() == 1) {
+		auto dir = args[0].find_last_of('/');
+		if(dir != std::string::npos)
+		{
+			test_dir = args[0].substr(0, dir + 1);
+			test_dir += "test_libtoolkit_area";
+		}
+		EXPECT_TRUE(!test_dir.empty());
+	} else {
+		test_dir = args[1];
+	}
+	std::cout << "test dir: " << test_dir << std::endl;
+	ASSERT_TRUE(ExistsDirA(test_dir.c_str()));
+	ASSERT_TRUE(ExistsFileA((test_dir + "/test_file3.txt").c_str()));
+	ASSERT_TRUE(ExistsFileA((test_dir + "/test_dir_3/test_dir_3_file_3.txt").c_str()));
+
+	/*create dir*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_3").c_str()));
+	ASSERT_TRUE(CreateDirRecursiveA((test_dir + "/new_dir_3/new_dir_3_1").c_str()));
+	EXPECT_TRUE(ExistsDirA((test_dir + "/new_dir_3/new_dir_3_1").c_str()));
+	/*remove dir*/
+	ASSERT_TRUE(RemoveDirRecursiveA((test_dir + "/new_dir_3").c_str()));
+	ASSERT_FALSE(ExistsDirA((test_dir + "/new_dir_3/new_dir_3_1").c_str()));
+	ASSERT_FALSE(ExistsDirA((test_dir + "/new_dir_3").c_str()));
+
+	/*copy dir*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_1").c_str()));
+	ASSERT_TRUE(CopyDirA((test_dir + "/test_dir_3").c_str(), (test_dir + "/new_dir_1").c_str()));
+	EXPECT_TRUE(ExistsDirA((test_dir + "/new_dir_1").c_str()));
+	ASSERT_TRUE(RemoveDirRecursiveA((test_dir + "/new_dir_1").c_str()));
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_1").c_str()));
+
+	/*backup*/
+	CopyDirA((test_dir + "/test_dir_2").c_str(), (test_dir + "/new_bk_2").c_str());
+	CopyDirA((test_dir + "/test_dir_3").c_str(), (test_dir + "/new_bk_3").c_str());
+
+	/*remove file*/
+	ASSERT_TRUE(RemoveFileA((test_dir + "/test_dir_3/test_dir_3_file_3.txt").c_str()));
+	EXPECT_FALSE(ExistsFileA((test_dir + "/test_dir_3/test_dir_3_file_3.txt").c_str()));
+
+	/*rollback new_dir_3*/
+	CopyDirA((test_dir + "/new_bk_3").c_str(), (test_dir + "/test_dir_3").c_str());
+	RemoveDirRecursiveA((test_dir + "/new_bk_3").c_str());
+
+	/*create grandson directory*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_2/new_dir_2_1").c_str()));
+	EXPECT_FALSE(CreateDirA((test_dir + "/new_dir_2/new_dir_2_1").c_str(), FALSE));
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_2").c_str()));
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_2/new_dir_2_1").c_str()));
+	EXPECT_TRUE(CreateDirA((test_dir + "/new_dir_2/new_dir_2_1").c_str(), TRUE));
+	EXPECT_TRUE(ExistsDirA((test_dir + "/new_dir_2/new_dir_2_1").c_str()));
+	/*remove dir*/
+	RemoveDirRecursiveA((test_dir + "/new_dir_2").c_str());
+	/*create parent dir*/
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_4").c_str()));
+	EXPECT_FALSE(CreateParentDirA((test_dir + "/new_dir_4/new_dir_4_1/new_dir_4_1_1").c_str(), FALSE));
+	EXPECT_FALSE(ExistsDirA((test_dir + "/new_dir_4").c_str()));
+	EXPECT_TRUE(CreateParentDirA((test_dir + "/new_dir_4/new_dir_4_1/new_dir_4_1_1").c_str(), TRUE));
+	EXPECT_TRUE(ExistsDirA((test_dir + "/new_dir_4/new_dir_4_1").c_str()));
+	RemoveDirRecursiveA((test_dir + "/new_dir_4").c_str());
+	/*clear dir*/
+	EXPECT_TRUE(ExistsFileA((test_dir + "/test_dir_2/test_sub_dir_2/test_sub_dir_2_file_1.txt").c_str()));
+	EXPECT_TRUE(ClearDirRecursiveA((test_dir + "/test_dir_2").c_str()));
+	EXPECT_FALSE(ExistsFileA((test_dir + "/test_dir_2/test_sub_dir_2/test_sub_dir_2_file_1.txt").c_str()));
+	EXPECT_TRUE(ExistsDirA((test_dir + "/test_dir_2/test_sub_dir_2").c_str()));
+
+	/*rollback new_dir_2*/
+	CopyDirA((test_dir + "/new_bk_2").c_str(), (test_dir + "/test_dir_2").c_str());
+	RemoveDirRecursiveA((test_dir + "/new_bk_2").c_str());
+}
+
+#endif

+ 8 - 0
libtoolkit/waitresult.h

@@ -1,3 +1,11 @@
+/*
+ * category: [unknown]
+ * apply status: no used
+ * edit status: no
+ * build status: no
+ * description: 
+ */
+
 #ifndef __WAITRESULT_H__
 #define __WAITRESULT_H__
 

+ 20 - 1
winpr/include/winpr/file.h

@@ -317,6 +317,7 @@ extern "C"
 
 	WINPR_API DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh);
 
+    WINPR_API BOOL GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime);
 	WINPR_API DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
 	                               DWORD dwMoveMethod);
 
@@ -378,7 +379,25 @@ extern "C"
 
 	WINPR_API BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName);
 
-	WINPR_API BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName);
+WINPR_API BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName);
+
+WINPR_API BOOL CopyFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName,BOOL bFailIfExists);
+
+WINPR_API BOOL CopyFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, BOOL bFailIfExists);
+
+//WINPR_API BOOL SystemTimeToFileTime(CONST SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime);
+
+//WINPR_API BOOL FileTimeToLocalFileTime(CONST FILETIME *lpFileTime, LPFILETIME lpLocalFileTime);
+
+WINPR_API BOOL LocalFileTimeToFileTime(CONST FILETIME *lpLocalFileTime, LPFILETIME lpFileTime);
+
+//WINPR_API BOOL FileTimeToSystemTime(CONST FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime);
+
+WINPR_API LONG CompareFileTime(CONST FILETIME *lpFileTime1, CONST FILETIME *lpFileTime2);
+
+WINPR_API BOOL FileTimeToDosDateTime(CONST FILETIME *lpFileTime, LPWORD lpFatDate, LPWORD lpFatTime);
+
+WINPR_API BOOL DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, LPFILETIME lpFileTime);
 
 #ifdef __cplusplus
 }

+ 26 - 5
winpr/include/winpr/string.h

@@ -50,13 +50,16 @@ extern "C"
 #define MB_USEGLYPHCHARS 0x00000004
 #define MB_ERR_INVALID_CHARS 0x00000008
 
-	WINPR_API char* _strdup(const char* strSource);
-	WINPR_API WCHAR* _wcsdup(const WCHAR* strSource);
+WINPR_API char* _strdup(const char* strSource);
+WINPR_API WCHAR* _wcsdup(const WCHAR* strSource);
 
-	WINPR_API int _stricmp(const char* string1, const char* string2);
-	WINPR_API int _strnicmp(const char* string1, const char* string2, size_t count);
+WINPR_API int _stricmp(const char* string1, const char* string2);
+WINPR_API int _strnicmp(const char* string1, const char* string2, size_t count);
 
-	WINPR_API int _wcscmp(const WCHAR* string1, const WCHAR* string2);
+WINPR_API int _wcscmp(const WCHAR* string1, const WCHAR* string2);
+WINPR_API int _wcsicmp(const WCHAR* string1, const WCHAR* string2);
+WINPR_API int _wcsnicmp(const WCHAR* string1, const WCHAR* string2, size_t count);
+WINPR_API int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count);
 
 	WINPR_API size_t _wcslen(const WCHAR* str);
 	WINPR_API size_t _wcsnlen(const WCHAR* str, size_t maxNumberOfElements);
@@ -179,6 +182,24 @@ extern "C"
 
 #define _scprintf(...) snprintf(NULL, 0, __VA_ARGS__)
 
+#define _scprintf(...) snprintf(NULL, 0, __VA_ARGS__)
+
+#if 0
+#define _vscprintf(...) vsnprintf(NULL, 0, __VA_ARGS__)
+#else
+	WINPR_API int _vscprintf(const char* format, va_list pargs);
+#endif
+	WINPR_API int _memicmp(const void* first, const void* last, unsigned int count);
+
+	WINPR_API VOID OutputDebugStringA(LPCSTR lpOutputString);
+	WINPR_API VOID OutputDebugStringW(LPCSTR lpOutputString);
+#ifdef UNICODE
+#define OutputDebugString	OutputDebugStringW
+#else
+#define OutputDebugString	OutputDebugStringA
+#endif
+
+
 	/* Unicode Conversion */
 
 	WINPR_API int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr,

+ 2 - 1
winpr/libwinpr/crt/CMakeLists.txt

@@ -20,7 +20,8 @@ set (CRT_FILES alignment.c
 	buffer.c
 	memory.c
 	unicode.c
-	string.c)
+	string.c
+	debug.c)
 
 if (NOT WITH_ICU)
 	set (CRT_FILES ${CRT_FILES}

+ 13 - 0
winpr/libwinpr/crt/debug.c

@@ -0,0 +1,13 @@
+#include "string.h"
+#include "../log.h"
+#define TAG WINPR_TAG("debug")
+
+VOID OutputDebugStringA(LPCSTR lpOutputString)
+{
+	WLog_DBG(TAG, lpOutputString);
+}
+
+VOID OutputDebugStringW(LPCSTR lpOutputString)
+{
+	WLog_DBG(TAG, lpOutputString);
+}

+ 36 - 0
winpr/libwinpr/crt/string.c

@@ -451,6 +451,42 @@ int lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2)
 	return value1 - value2;
 }
 
+
+int _vscprintf(const char* format, va_list pargs) 
+{
+	int retval;
+	va_list argcopy;
+	va_copy(argcopy, pargs);
+	retval = vsnprintf(NULL, 0, format, argcopy);
+	va_end(argcopy);
+	return retval;
+}
+
+int _memicmp(const void* first, const void* last, unsigned int count)
+{
+	unsigned char f, l;
+
+	if (count) do {
+		f = *((unsigned char*)first);
+		l = *((unsigned char*)last);
+		if (f != l) {
+			if (f >= 'A' && f <= 'Z') {
+				f = f - 'A' + 'a';
+			}
+			if (l >= 'A' && l <= 'Z') {
+				l = l - 'A' + 'a';
+			}
+			if (f != l) {
+				return f - l;
+			}
+		}
+		first = (char*)first + 1;
+		last = (char*)last + 1;
+	} while (--count);
+
+	return 0;
+}
+
 #endif
 
 int ConvertLineEndingToLF(char* str, int size)

+ 36 - 1
winpr/libwinpr/file/file.c

@@ -57,6 +57,9 @@
 #include <sys/statvfs.h>
 #endif
 
+#define EPOCH_DIFF_MACRO 11644473600LL
+#define STAT_TIME_TO_FILETIME(_t) (((UINT64)(_t) + EPOCH_DIFF_MACRO) * 10000000LL)
+
 static BOOL FileIsHandled(HANDLE handle)
 {
 	WINPR_FILE* pFile = (WINPR_FILE*)handle;
@@ -584,6 +587,36 @@ static BOOL FileSetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
 	return TRUE;
 }
 
+static BOOL FileGetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime)
+{
+	int rc;
+	UINT64 ft;
+	struct stat buf;
+	WINPR_FILE* pFile = (WINPR_FILE*)hFile;
+	if(!hFile) {
+		return FALSE;
+	}
+	rc = fstat(fileno(pFile->fp), &buf);
+	if(rc < 0) {
+		return FALSE;
+	}
+	if(lpCreationTime) {
+		ft = STAT_TIME_TO_FILETIME(buf.st_ctime);
+		lpCreationTime->dwHighDateTime = ((UINT64)ft) >> 32ULL;
+		lpCreationTime->dwLowDateTime = ft & 0xFFFFFFFF;
+	}
+	if(lpLastAccessTime) {
+		ft = STAT_TIME_TO_FILETIME(buf.st_atime);
+		lpLastAccessTime->dwHighDateTime = ((UINT64)ft) >> 32ULL;
+		lpLastAccessTime->dwLowDateTime = ft & 0xFFFFFFFF;
+	}
+	if(lpLastWriteTime) {
+		ft = STAT_TIME_TO_FILETIME(buf.st_mtime);
+		lpLastWriteTime->dwHighDateTime = ((UINT64)ft) >> 32ULL;
+		lpLastWriteTime->dwLowDateTime = ft & 0xFFFFFFFF;
+	}
+	return TRUE;
+}
 static HANDLE_OPS fileOps = { FileIsHandled,
 	                          FileCloseHandle,
 	                          FileGetFd,
@@ -603,7 +636,9 @@ static HANDLE_OPS fileOps = { FileIsHandled,
 	                          FileLockFileEx,
 	                          FileUnlockFile,
 	                          FileUnlockFileEx,
-	                          FileSetFileTime };
+								FileSetFileTime,
+								FileGetFileTime
+							};
 
 static HANDLE_OPS shmOps = {
 	FileIsHandled, FileCloseHandle,

+ 190 - 2
winpr/libwinpr/file/generic.c

@@ -62,6 +62,8 @@
 
 #ifdef ANDROID
 #include <sys/vfs.h>
+#include <winpr/timezone.h>
+
 #else
 #include <sys/statvfs.h>
 #endif
@@ -71,6 +73,7 @@
 #include "../pipe/pipe.h"
 
 #include "file.h"
+#include <winpr/timezone.h>
 
 /**
  * api-ms-win-core-file-l1-2-0.dll:
@@ -622,6 +625,22 @@ DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
 	return 0;
 }
 
+BOOL GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime)
+{
+	ULONG Type;
+	WINPR_HANDLE* handle;
+	if(hFile == INVALID_HANDLE_VALUE) {
+		return FALSE;
+	}
+	if(!winpr_Handle_GetInfo(hFile, &Type, &handle)) {
+		return FALSE;
+	}
+	handle = (WINPR_HANDLE*)hFile;
+	if(handle->ops->GetFileTime)
+		return handle->ops->GetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
+	WLog_ERR(TAG, "GetFileTime operation not implemented");
+	return FALSE;
+}
 DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
                      DWORD dwMoveMethod)
 {
@@ -1159,9 +1178,10 @@ BOOL FindClose(HANDLE hFindFile)
 
 BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
 {
-	if (!mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR))
+	if (!mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR)) {
 		return TRUE;
-
+	}
+	fprintf(stderr, "mkdir %s failed: %s\n", lpPathName, strerror(errno));
 	return FALSE;
 }
 
@@ -1276,6 +1296,174 @@ BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
 	return MoveFileExW(lpExistingFileName, lpNewFileName, 0);
 }
 
+BOOL CopyFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName,BOOL bFailIfExists)
+{
+	char buffer[1024];
+	int len, n;
+	int fd_to_read, fd_to_write;
+
+	fd_to_read = open(lpExistingFileName, O_RDONLY);
+	if(fd_to_read == -1) {
+		WLog_WARN(TAG, "open the source file %s failed: %s", lpExistingFileName, strerror(errno));
+		SetLastError(ERROR_FILE_NOT_FOUND);
+		return FALSE;
+	}
+	fd_to_write = open(lpNewFileName, O_RDWR|O_CREAT, S_IWUSR|S_IRUSR|S_IXUSR);
+	if(fd_to_write == -1) {
+		SetLastError(ERROR_CREATE_FAILED);
+		close(fd_to_read);
+		return FALSE;
+	}
+	while((len = read(fd_to_read, buffer, 1024))) {
+		n = write(fd_to_write, buffer, len);
+		if(n != len) {
+			SetLastError(ERROR_PARTIAL_COPY);
+			close(fd_to_read);
+			close(fd_to_write);
+			return FALSE;
+		}
+	}
+	close(fd_to_read);
+	close(fd_to_write);
+	return TRUE;
+}
+
+BOOL CopyFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, BOOL bFailIfExists)
+{
+	LPSTR lpCExistingFileName;
+	LPSTR lpCNewFileName;
+	BOOL ret;
+
+	if (ConvertFromUnicode(CP_UTF8, 0, lpExistingFileName, -1, &lpCExistingFileName, 0, NULL,
+						   NULL) <= 0)
+	{
+		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+		return FALSE;
+	}
+
+	if (ConvertFromUnicode(CP_UTF8, 0, lpNewFileName, -1, &lpCNewFileName, 0, NULL, NULL) <= 0)
+	{
+		free(lpCExistingFileName);
+		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+		return FALSE;
+	}
+
+	ret = CopyFileA(lpCExistingFileName, lpCNewFileName, bFailIfExists);
+	free(lpCNewFileName);
+	free(lpCExistingFileName);
+	return ret;
+}
+
+//BOOL SystemTimeToFileTime(CONST SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime)
+//{
+//	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+//	return FALSE;
+//}
+
+#if 0
+BOOL FileTimeToLocalFileTime(CONST FILETIME *lpFileTime, LPFILETIME lpLocalFileTime)
+{
+	LARGE_INTEGER local, utc;
+    LONG bias;
+    TIME_ZONE_INFORMATION tz;
+    DWORD dwRes;
+
+	utc.u.LowPart = lpFileTime->dwLowDateTime;
+	utc.u.HighPart = lpFileTime->dwHighDateTime;
+
+	{
+        dwRes = GetTimeZoneInformation(&tz);
+		if(dwRes != 0) {
+			bias = tz.Bias;
+			/* 1 ... TIME_ZONE_ID_STANDARD
+ 			 * 2 ... TIME_ZONE_ID_DAYLIGHT
+ 			 */
+			bias += dwRes == 1 ? tz.StandardBias : tz.DaylightBias;
+			bias *= SECSPERMIN;
+		}
+
+		local.QuadPart = utc.QuadPart - bias * (LONGLONG)TICKSPERSEC;
+	}
+
+	lpLocalFileTime->dwLowDateTime = local.u.LowPart;
+	lpLocalFileTime->dwHighDateTime = local.u.HighPart;
+
+	return TRUE;
+}
+#else
+
+//BOOL FileTimeToLocalFileTime(CONST FILETIME *lpFileTime, LPFILETIME lpLocalFileTime)
+//{
+//	LARGE_INTEGER local, utc;
+//	LONG bias;
+//	TIME_ZONE_INFORMATION tz;
+//	DWORD dwRes;
+//
+//	utc.u.LowPart = lpFileTime->dwLowDateTime;
+//	utc.u.HighPart = lpFileTime->dwHighDateTime;
+//
+//	{
+//		dwRes = GetTimeZoneInformation(&tz);
+//		if(dwRes != 0) {
+//			bias = tz.Bias;
+//			/* 1 ... TIME_ZONE_ID_STANDARD
+// 			 * 2 ... TIME_ZONE_ID_DAYLIGHT
+// 			 */
+//			bias += dwRes == 2 ? tz.DaylightBias : tz.StandardBias;
+//			bias *= SECSPERMIN;
+//		} else {
+//			WLog_DBG(TAG, "GetTimeZoneInformation return zero !");
+//            bias = tz.Bias;
+//            bias += tz.StandardBias;
+//            bias *= SECSPERMIN;
+//		}
+//
+//		local.QuadPart = utc.QuadPart - bias * (LONGLONG)TICKSPERSEC;
+//	}
+//
+//	lpLocalFileTime->dwLowDateTime = local.u.LowPart;
+//	lpLocalFileTime->dwHighDateTime = local.u.HighPart;
+//
+//	return TRUE;
+//}
+#endif
+
+
+
+BOOL LocalFileTimeToFileTime(CONST FILETIME *lpLocalFileTime, LPFILETIME lpFileTime)
+{
+    WLog_ERR(TAG, "TODO: %s not implemented!", __FUNCTION__);
+	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+	return FALSE;
+}
+
+//BOOL FileTimeToSystemTime(CONST FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime)
+//{
+//	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+//	return FALSE;
+//}
+
+LONG CompareFileTime(CONST FILETIME *lpFileTime1, CONST FILETIME *lpFileTime2)
+{
+    WLog_ERR(TAG, "TODO: %s not implemented!", __FUNCTION__);
+	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+	return FALSE;
+}
+
+BOOL FileTimeToDosDateTime(CONST FILETIME *lpFileTime, LPWORD lpFatDate, LPWORD lpFatTime)
+{
+    WLog_ERR(TAG, "TODO: %s not implemented!", __FUNCTION__);
+	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+	return FALSE;
+}
+
+BOOL DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, LPFILETIME lpFileTime)
+{
+    WLog_ERR(TAG, "TODO: %s not implemented!", __FUNCTION__);
+	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+	return FALSE;
+}
+
 #endif
 
 /* Extended API */

+ 3 - 0
winpr/libwinpr/handle/handle.h

@@ -83,6 +83,8 @@ typedef BOOL (*pcUnlockFileEx)(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBy
                                DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped);
 typedef BOOL (*pcSetFileTime)(HANDLE hFile, const FILETIME* lpCreationTime,
                               const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime);
+typedef BOOL(*pcGetFileTime)(HANDLE hFile, LPFILETIME lpCreationTime,
+	LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime);
 
 typedef struct _HANDLE_OPS
 {
@@ -106,6 +108,7 @@ typedef struct _HANDLE_OPS
 	pcUnlockFile UnlockFile;
 	pcUnlockFileEx UnlockFileEx;
 	pcSetFileTime SetFileTime;
+	pcGetFileTime GetFileTime;
 } HANDLE_OPS;
 
 struct winpr_handle