@@ -94,12 +94,16 @@ SQLITE_EXTENSION_INIT1
9494# include <utime.h>
9595# include <sys/time.h>
9696# define STRUCT_STAT struct stat
97+ # include <limits.h>
98+ # include <stdlib.h>
9799#else
98100# include "windirent.h"
99101# include <direct.h>
100102# define STRUCT_STAT struct _stat
101103# define chmod (path ,mode ) fileio_chmod(path,mode)
102104# define mkdir (path ,mode ) fileio_mkdir(path)
105+ extern LPWSTR sqlite3_win32_utf8_to_unicode (const char * );
106+ extern char * sqlite3_win32_unicode_to_utf8 (LPCWSTR );
103107#endif
104108#include <time.h>
105109#include <errno.h>
@@ -131,12 +135,9 @@ SQLITE_EXTENSION_INIT1
131135*/
132136#if defined(_WIN32 ) || defined(WIN32 )
133137static int fileio_chmod (const char * zPath , int pmode ){
134- sqlite3_int64 sz = strlen (zPath );
135- wchar_t * b1 = sqlite3_malloc64 ( (sz + 1 )* sizeof (b1 [0 ]) );
136138 int rc ;
139+ wchar_t * b1 = sqlite3_win32_utf8_to_unicode (zPath );
137140 if ( b1 == 0 ) return -1 ;
138- sz = MultiByteToWideChar (CP_UTF8 , 0 , zPath , sz , b1 , sz );
139- b1 [sz ] = 0 ;
140141 rc = _wchmod (b1 , pmode );
141142 sqlite3_free (b1 );
142143 return rc ;
@@ -148,12 +149,9 @@ static int fileio_chmod(const char *zPath, int pmode){
148149*/
149150#if defined(_WIN32 ) || defined(WIN32 )
150151static int fileio_mkdir (const char * zPath ){
151- sqlite3_int64 sz = strlen (zPath );
152- wchar_t * b1 = sqlite3_malloc64 ( (sz + 1 )* sizeof (b1 [0 ]) );
153152 int rc ;
153+ wchar_t * b1 = sqlite3_win32_utf8_to_unicode (zPath );
154154 if ( b1 == 0 ) return -1 ;
155- sz = MultiByteToWideChar (CP_UTF8 , 0 , zPath , sz , b1 , sz );
156- b1 [sz ] = 0 ;
157155 rc = _wmkdir (b1 );
158156 sqlite3_free (b1 );
159157 return rc ;
@@ -266,50 +264,7 @@ static sqlite3_uint64 fileTimeToUnixTime(
266264
267265 return (fileIntervals .QuadPart - epochIntervals .QuadPart ) / 10000000 ;
268266}
269-
270-
271- #if defined(FILEIO_WIN32_DLL ) && (defined(_WIN32 ) || defined(WIN32 ))
272- # /* To allow a standalone DLL, use this next replacement function: */
273- # undef sqlite3_win32_utf8_to_unicode
274- # define sqlite3_win32_utf8_to_unicode utf8_to_utf16
275- #
276- LPWSTR utf8_to_utf16 (const char * z ){
277- int nAllot = MultiByteToWideChar (CP_UTF8 , 0 , z , -1 , NULL , 0 );
278- LPWSTR rv = sqlite3_malloc (nAllot * sizeof (WCHAR ));
279- if ( rv != 0 && 0 < MultiByteToWideChar (CP_UTF8 , 0 , z , -1 , rv , nAllot ) )
280- return rv ;
281- sqlite3_free (rv );
282- return 0 ;
283- }
284- #endif
285-
286- /*
287- ** This function attempts to normalize the time values found in the stat()
288- ** buffer to UTC. This is necessary on Win32, where the runtime library
289- ** appears to return these values as local times.
290- */
291- static void statTimesToUtc (
292- const char * zPath ,
293- STRUCT_STAT * pStatBuf
294- ){
295- HANDLE hFindFile ;
296- WIN32_FIND_DATAW fd ;
297- LPWSTR zUnicodeName ;
298- extern LPWSTR sqlite3_win32_utf8_to_unicode (const char * );
299- zUnicodeName = sqlite3_win32_utf8_to_unicode (zPath );
300- if ( zUnicodeName ){
301- memset (& fd , 0 , sizeof (WIN32_FIND_DATAW ));
302- hFindFile = FindFirstFileW (zUnicodeName , & fd );
303- if ( hFindFile != NULL ){
304- pStatBuf -> st_ctime = (time_t )fileTimeToUnixTime (& fd .ftCreationTime );
305- pStatBuf -> st_atime = (time_t )fileTimeToUnixTime (& fd .ftLastAccessTime );
306- pStatBuf -> st_mtime = (time_t )fileTimeToUnixTime (& fd .ftLastWriteTime );
307- FindClose (hFindFile );
308- }
309- sqlite3_free (zUnicodeName );
310- }
311- }
312- #endif
267+ #endif /* _WIN32 */
313268
314269/*
315270** This function is used in place of stat(). On Windows, special handling
@@ -321,14 +276,22 @@ static int fileStat(
321276 STRUCT_STAT * pStatBuf
322277){
323278#if defined(_WIN32 )
324- sqlite3_int64 sz = strlen (zPath );
325- wchar_t * b1 = sqlite3_malloc64 ( (sz + 1 )* sizeof (b1 [0 ]) );
326279 int rc ;
280+ wchar_t * b1 = sqlite3_win32_utf8_to_unicode (zPath );
327281 if ( b1 == 0 ) return 1 ;
328- sz = MultiByteToWideChar (CP_UTF8 , 0 , zPath , sz , b1 , sz );
329- b1 [sz ] = 0 ;
330282 rc = _wstat (b1 , pStatBuf );
331- if ( rc == 0 ) statTimesToUtc (zPath , pStatBuf );
283+ if ( rc == 0 ){
284+ HANDLE hFindFile ;
285+ WIN32_FIND_DATAW fd ;
286+ memset (& fd , 0 , sizeof (WIN32_FIND_DATAW ));
287+ hFindFile = FindFirstFileW (b1 , & fd );
288+ if ( hFindFile != NULL ){
289+ pStatBuf -> st_ctime = (time_t )fileTimeToUnixTime (& fd .ftCreationTime );
290+ pStatBuf -> st_atime = (time_t )fileTimeToUnixTime (& fd .ftLastAccessTime );
291+ pStatBuf -> st_mtime = (time_t )fileTimeToUnixTime (& fd .ftLastWriteTime );
292+ FindClose (hFindFile );
293+ }
294+ }
332295 sqlite3_free (b1 );
333296 return rc ;
334297#else
@@ -460,7 +423,6 @@ static int writeFile(
460423
461424 if ( mtime >=0 ){
462425#if defined(_WIN32 )
463- #if !SQLITE_OS_WINRT
464426 /* Windows */
465427 FILETIME lastAccess ;
466428 FILETIME lastWrite ;
@@ -491,7 +453,6 @@ static int writeFile(
491453 }else {
492454 return 1 ;
493455 }
494- #endif
495456#elif defined(AT_FDCWD ) && 0 /* utimensat() is not universally available */
496457 /* Recent unix */
497458 struct timespec times [2 ];
@@ -1095,6 +1056,154 @@ static int fsdirRegister(sqlite3 *db){
10951056# define fsdirRegister (x ) SQLITE_OK
10961057#endif
10971058
1059+ /*
1060+ ** This version of realpath() works on any system. The string
1061+ ** returned is held in memory allocated using sqlite3_malloc64().
1062+ ** The caller is responsible for calling sqlite3_free().
1063+ */
1064+ static char * portable_realpath (const char * zPath ){
1065+ #if !defined(_WIN32 ) /* BEGIN unix */
1066+
1067+ char * zOut = 0 ; /* Result */
1068+ char * z ; /* Temporary buffer */
1069+ #if defined(PATH_MAX )
1070+ char zBuf [PATH_MAX + 1 ]; /* Space for the temporary buffer */
1071+ #endif
1072+
1073+ if ( zPath == 0 ) return 0 ;
1074+ #if defined(PATH_MAX )
1075+ z = realpath (zPath , zBuf );
1076+ if ( z ){
1077+ zOut = sqlite3_mprintf ("%s" , zBuf );
1078+ }
1079+ #endif /* defined(PATH_MAX) */
1080+ if ( zOut == 0 ){
1081+ /* Try POSIX.1-2008 malloc behavior */
1082+ z = realpath (zPath , NULL );
1083+ if ( z ){
1084+ zOut = sqlite3_mprintf ("%s" , z );
1085+ free (z );
1086+ }
1087+ }
1088+ return zOut ;
1089+
1090+ #else /* End UNIX, Begin WINDOWS */
1091+
1092+ wchar_t * zPath16 ; /* UTF16 translation of zPath */
1093+ char * zOut = 0 ; /* Result */
1094+ wchar_t * z = 0 ; /* Temporary buffer */
1095+
1096+ if ( zPath == 0 ) return 0 ;
1097+
1098+ zPath16 = sqlite3_win32_utf8_to_unicode (zPath );
1099+ if ( zPath16 == 0 ) return 0 ;
1100+ z = _wfullpath (NULL , zPath16 , 0 );
1101+ sqlite3_free (zPath16 );
1102+ if ( z ){
1103+ zOut = sqlite3_win32_unicode_to_utf8 (z );
1104+ free (z );
1105+ }
1106+ return zOut ;
1107+
1108+ #endif /* End WINDOWS, Begin common code */
1109+ }
1110+
1111+ /*
1112+ ** SQL function: realpath(X)
1113+ **
1114+ ** Try to convert file or pathname X into its real, absolute pathname.
1115+ ** Return NULL if unable.
1116+ **
1117+ ** The file or directory X is not required to exist. The answer is formed
1118+ ** by calling system realpath() on the prefix of X that does exist and
1119+ ** appending the tail of X that does not (yet) exist.
1120+ */
1121+ static void realpathFunc (
1122+ sqlite3_context * context ,
1123+ int argc ,
1124+ sqlite3_value * * argv
1125+ ){
1126+ const char * zPath ; /* Original input path */
1127+ char * zCopy ; /* An editable copy of zPath */
1128+ char * zOut ; /* The result */
1129+ char cSep = 0 ; /* Separator turned into \000 */
1130+ size_t len ; /* Prefix length before cSep */
1131+ #ifdef _WIN32
1132+ const int isWin = 1 ;
1133+ #else
1134+ const int isWin = 0 ;
1135+ #endif
1136+
1137+ (void )argc ;
1138+ zPath = (const char * )sqlite3_value_text (argv [0 ]);
1139+ if ( zPath == 0 ) return ;
1140+ if ( zPath [0 ]== 0 ) zPath = "." ;
1141+ zCopy = sqlite3_mprintf ("%s" ,zPath );
1142+ len = strlen (zCopy );
1143+ while ( len > 1 && (zCopy [len - 1 ]== '/' || (isWin && zCopy [len - 1 ]== '\\' )) ){
1144+ len -- ;
1145+ }
1146+ zCopy [len ] = 0 ;
1147+ while ( 1 /*exit-by-break*/ ){
1148+ zOut = portable_realpath (zCopy );
1149+ zCopy [len ] = cSep ;
1150+ if ( zOut ){
1151+ if ( cSep ){
1152+ zOut = sqlite3_mprintf ("%z%s" ,zOut ,& zCopy [len ]);
1153+ }
1154+ break ;
1155+ }else {
1156+ size_t i = len - 1 ;
1157+ while ( i > 0 ){
1158+ if ( zCopy [i ]== '/' || (isWin && zCopy [i ]== '\\' ) ) break ;
1159+ i -- ;
1160+ }
1161+ if ( i <=0 ){
1162+ if ( zCopy [0 ]== '/' ){
1163+ zOut = zCopy ;
1164+ zCopy = 0 ;
1165+ }else if ( (zOut = portable_realpath ("." ))!= 0 ){
1166+ zOut = sqlite3_mprintf ("%z/%s" , zOut , zCopy );
1167+ }
1168+ break ;
1169+ }
1170+ cSep = zCopy [i ];
1171+ zCopy [i ] = 0 ;
1172+ len = i ;
1173+ }
1174+ }
1175+ sqlite3_free (zCopy );
1176+ if ( zOut ){
1177+ /* Simplify any "/./" or "/../" that might have snuck into the
1178+ ** pathname due to appending of zCopy. We only have to consider
1179+ ** unix "/" separators, because the _wfilepath() system call on
1180+ ** Windows will have already done this simplification for us. */
1181+ size_t i , j , n ;
1182+ n = strlen (zOut );
1183+ for (i = j = 0 ; i < n ; i ++ ){
1184+ if ( zOut [i ]== '/' ){
1185+ if ( zOut [i + 1 ]== '/' ) continue ;
1186+ if ( zOut [i + 1 ]== '.' && i + 2 < n && zOut [i + 2 ]== '/' ){
1187+ i += 1 ;
1188+ continue ;
1189+ }
1190+ if ( zOut [i + 1 ]== '.' && i + 3 < n && zOut [i + 2 ]== '.' && zOut [i + 3 ]== '/' ){
1191+ while ( j > 0 && zOut [j - 1 ]!= '/' ){ j -- ; }
1192+ if ( j > 0 ){ j -- ; }
1193+ i += 2 ;
1194+ continue ;
1195+ }
1196+ }
1197+ zOut [j ++ ] = zOut [i ];
1198+ }
1199+ zOut [j ] = 0 ;
1200+
1201+ /* Return the result */
1202+ sqlite3_result_text (context , zOut , -1 , sqlite3_free );
1203+ }
1204+ }
1205+
1206+
10981207#ifndef SQLITE_API
10991208#define SQLITE_API
11001209#endif
@@ -1122,5 +1231,10 @@ int sqlite3_fileio_init(
11221231 if ( rc == SQLITE_OK ){
11231232 rc = fsdirRegister (db );
11241233 }
1234+ if ( rc == SQLITE_OK ){
1235+ rc = sqlite3_create_function (db , "realpath" , 1 ,
1236+ SQLITE_UTF8 , 0 ,
1237+ realpathFunc , 0 , 0 );
1238+ }
11251239 return rc ;
11261240}
0 commit comments